Что хочет знать интервьюер, когда спрашивает о Webpack

Webpack
Что хочет знать интервьюер, когда спрашивает о Webpack

передняя часть Seewo ENOW

Официальный сайт компании:CVTE (Гуанчжоу CVTE)

Команда: enow team центра программных платформ Seewo для будущего образования в рамках CVTE

Автор этой статьи:

温广名片2.png

предисловие

В сегодняшнем все более сложном интерфейсном проектировании инструменты упаковки модулей играют все более важную роль в наших разработках, среди которыхwebpackЭто один из самых популярных упаковочных инструментов.

Говоря оwebpack, многие мелкие партнеры могут показаться как знакомыми, так и незнакомыми, знакомыми, потому что мы используем их почти в каждом проекте, и потому чтоwebpackСложная конфигурация и различные функции незнакомы. Особенно, когда мы используем что-то вродеumi.jsТакие фреймворки приложений также помогают нам инкапсулировать конфигурацию веб-пакета другим уровнем.webpackПрирода его кажется более далекой и непостижимой от нас.

Когда интервьюер спрашивает, понимаешь ли тыwebpackКогда, может быть, ты сможешь сказать строку знакомуюwebpack loaderиpluginНазвание плагина и даже ряд конфигураций могут быть загружены и упакованы по требованию.Знаете ли вы его механизм работы и принцип реализации?Тогда мы будем исследовать его вместе сегодня.webpackграницы компетенции, постарайтесь понятьwebpackНекоторые процессы и принципы реализации, отказываются делатьAPIинженер.

CgqCHl6pSFmAC5UzAAEwx63IBwE024.png

Вы знаете, что делает веб-пакет?

Это не сложно понять из описания на официальном сайте.webpackНа самом деле он выполняет следующие функции:

  • Упаковка модуля. Файлы разных модулей могут быть упакованы и объединены вместе, а ссылки между ними могут быть гарантированно правильными и выполняться упорядоченным образом. Используя упаковку, мы можем свободно делить файловые модули в соответствии с нашими собственными делами во время разработки, чтобы обеспечить ясность и читабельность структуры проекта.

  • Совместимость с компиляцией. В «древние времена» фронтенда написание от руки кучи совместимого с браузером кода всегда вызывало онемение у фронтенд-инженеров, но сегодня эта проблема сильно ослабла.webpackизLoaderМеханизм не только может помочь нам сделать с кодомpolyfill, вы также можете скомпилировать преобразования, такие как.less, .vue, .jsxЭтот тип файла формата, который не распознается браузером, позволяет нам использовать новые функции и новый синтаксис для разработки во время разработки, повышая эффективность разработки.

  • расширение емкости. пройти черезwebpackизPluginМеханизм, на основе реализации модульной упаковки и совместимости компиляции, мы можем дополнительно реализовать ряд функций, таких как загрузка по требованию, сжатие кода и т. д., чтобы помочь нам еще больше повысить степень автоматизации, инженерную эффективность и качество выход упаковки.

Расскажите о принципе работы модульной упаковки?

Если интервьюер спрашивает васWebpackВы понимаете, как эти модули объединяются вместе и гарантированно работают правильно?

Во-первых, мы должны кратко понятьwebpackВесь процесс упаковки:

  • 1. Читатьwebpackпараметры конфигурации;
  • 2. Стартwebpack,СоздайтеCompilerобъект и начать разбор элемента;
  • 3. Из входного файла (entry) начать синтаксический анализ и найти импортированные зависимые модули, рекурсивно пройти анализ и сформировать дерево зависимостей;
  • 4. Используйте соответствующие файлы модулей для зависимых файлов модулей разных типов файлов.LoaderСкомпилируйте и, наконец, конвертируйте вJavascriptдокумент;
  • 5. В течение всего процессаwebpackНекоторые из них будут выбрасываться через модель публикации-подписки.hookswebpackПлагин может прослушивать эти ключевые узлы событий, выполнять задачи плагина, а затем достигать цели вмешательства в результаты вывода.

Разбор и создание файлов — относительно сложный процесс.webpackИсходный код в основном зависит отcompilerиcompilationРеализованы два основных объекта.

compilerОбъект представляет собой глобальный синглтон, отвечающий за управление всемwebpackПакетный процесс сборки.compilationОбъект является объектом контекста каждой сборки, он содержит всю информацию, необходимую для текущей сборки, каждого горячего обновления и сборки,compilerбудет регенерировать новыйcompilationОбъект, отвечающий за процесс сборки этого обновления.

Зависимости между каждым модулем зависят отASTсинтаксическое дерево. Каждый файл модуля передаетсяLoaderПосле завершения синтаксического анализа он пройдетacornбиблиотека для генерации кода модуляASTСинтаксическое дерево, через синтаксическое дерево можно проанализировать, есть ли у этого модуля зависимые модули, а затем продолжить выполнение компиляции и анализа следующего модуля в цикле.

наконец-тоWebpackв упаковкеbundleфайл представляет собойIIFEфункция исполнения.

// webpack 5 打包的bundle文件内容

(() => { // webpackBootstrap
    var __webpack_modules__ = ({
        'file-A-path': ((modules) => { // ... })
        'index-file-path': ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { // ... })
    })
    
    // The module cache
    var __webpack_module_cache__ = {};
    
    // The require function
    function __webpack_require__(moduleId) {
        // Check if module is in cache
        var cachedModule = __webpack_module_cache__[moduleId];
        if (cachedModule !== undefined) {
                return cachedModule.exports;
        }
        // Create a new module (and put it into the cache)
        var module = __webpack_module_cache__[moduleId] = {
                // no module.id needed
                // no module.loaded needed
                exports: {}
        };

        // Execute the module function
        __webpack_modules__[moduleId](module, module.exports, __webpack_require__);

        // Return the exports of the module
        return module.exports;
    }
    
    // startup
    // Load entry module and return exports
    // This entry module can't be inlined because the eval devtool is used.
    var __webpack_exports__ = __webpack_require__("./src/index.js");
})

иwebpack4по сравнению с,webpack5Упакованный комплект был довольно обтекаемым. упаковка сверхуdemo, всего три переменных и один метод функции во всей функции непосредственного выполнения,__webpack_modules__Сохраняет содержимое JS каждого скомпилированного файлового модуля,__webpack_module_cache__ Используется для кэширования модулей,__webpack_require__даWebpackНабор функций импорта зависимостей, реализованных внутри. Последнее предложение является отправной точкой выполнения кода, начиная с входного файла и запуская весь проект.

Из них стоит упомянуть, что__webpack_require__Функция введения модуля, когда мы разрабатываем модульно, мы обычно используемES ModuleилиCommonJSСпецификация экспорта/импорта зависимых модулей,webpackПри упаковке и компиляции он будет единообразно заменен собственным__webpack_require__Реализовать импорт и экспорт модулей, реализовать механизм кэширования модулей и сгладить некоторые различия между спецификациями разных модулей.

Вы знаете, что такое sourceMap?

упомянулsourceMap, многие друзья могут сразу подумать оWebpackвнутри конфигурацииdevtoolпараметры и соответствующиеeval,eval-cheap-source-mapи т.д. необязательные значения и что они означают. Помимо того, что мы знаем разницу между разными параметрами и разницу в производительности, мы также можем узнать об этом вместе.sourceMapспособ реализации.

sourceMapЭто технология, которая отображает скомпилированный, упакованный и сжатый код обратно в исходный код.Поскольку упакованный и сжатый код не читается, как только сообщается об ошибке или возникает проблема во время разработки, она находится непосредственно в запутанном коде.debugПроблемы могут привести к очень плохому опыту,sourceMapЭто может помочь нам быстро определить местонахождение исходного кода и повысить эффективность нашей разработки.sourceMapне совсемWebpackуникальная функция, ноWebpackслужба поддержкиsourceMap,рисунокJQueryтакже поддерживаетsouceMap.

Поскольку это сопоставление исходного кода, необходимо иметь файл сопоставления, чтобы отметить расположение соответствующего исходного кода в запутанном коде.Обычно этот файл сопоставления начинается с.mapВ итоге структура данных внутри выглядит так:

{
  "version" : 3,                          // Source Map版本
  "file": "out.js",                       // 输出文件(可选)
  "sourceRoot": "",                       // 源文件根目录(可选)
  "sources": ["foo.js", "bar.js"],        // 源文件列表
  "sourcesContent": [null, null],         // 源内容列表(可选,和源文件列表顺序一致)
  "names": ["src", "maps", "are", "fun"], // mappings使用的符号名称列表
  "mappings": "A,AAAB;;ABCDE;"            // 带有编码映射数据的字符串
}

вmappingsДанные имеют следующие правила:

  • Каждая группа строк в make-файле отделяется ";";
  • Каждый абзац отделяется ",";
  • Каждый сегмент состоит из 1, 4 или 5 полей переменной длины;

С этим файлом сопоставления нам просто нужно добавить этот комментарий в самый конец нашего кода сжатия, чтобы sourceMap вступил в силу:

//# sourceURL=/path/to/file.js.map

С этим комментарием браузер пройдетsourceURLЧтобы получить этот файл сопоставления, после его синтаксического анализа с помощью интерпретатора реализуйте сопоставление между исходным кодом и запутанным кодом. Таким образом, sourceMap на самом деле является технологией, требующей поддержки браузера.

Если мы внимательно посмотрим на файлы пакетов, упакованные webpack, мы обнаружим, что в файле по умолчаниюdevelopmentВ режиме разработки каждый_webpack_modules__В конце кода файлового модуля будет добавлено//# sourceURL=webpack://file-path?, чтобы реализовать поддержку sourceMap.

Генерация таблицы сопоставления sourceMap имеет набор относительно сложных правил. Заинтересованные партнеры могут прочитать следующие статьи, которые помогут понять принцип и реализацию sourceMap:

Исследование по принципу Source Map

Исходные карты под капотом — VLQ, Base64 и Yoda

Вы написали Loader? Кратко опишите идею написания загрузчика?

Из приведенного выше кода упаковки мы можем узнать, чтоWebpackОкончательный упакованный результат представляет собойJavascriptкод, на самом деле вWebpackВнутреннее значение по умолчанию может обрабатывать толькоJSКод модуля в процессе упаковки по умолчанию будет обрабатывать все встречающиеся файлы какJavaScriptкод анализируется, поэтому, когда проект имеет не-JSПри вводе файла нам нужно преобразовать его по мере необходимости, прежде чем приступить к задаче упаковки, которая такжеLoaderналичие механизма.

LoaderИспользование конфигурации, с которым мы должны быть хорошо знакомы:

// webpack.config.js
module.exports = {
  // ...other config
  module: {
    rules: [
      {
        test: /^your-regExp$/,
        use: [
          {
             loader: 'loader-name-A',
          }, 
          {
             loader: 'loader-name-B',
          }
        ]
      },
    ]
  }
}

Из конфигурации видно, что для каждого типа файлаloaderОн поддерживает несколько конфигураций в виде массивов, поэтому, когдаWebpackПри преобразовании этого типа файла каждый вызов будет цепочкой по порядку.loader,ПредыдущийloaderВозвращенный контент будет использоваться в качестве следующегоloaderввод. следовательноloaderРазработка должна соответствовать некоторым спецификациям, например, возвращаемое значение должно быть стандартным.JSстрока кода, чтобы гарантировать следующийloaderМожет работать правильно, и в то же время нужно строго следовать «единой ответственности» в разработке, заботиться только оloaderвывод и соответствующий вывод.

loaderв функцииthisконтекст поwebpackдоступный, черезthisСвязанные свойства, предоставляемые объектом, получают текущийloaderРазличные информационные данные, необходимые, по сути, этоthisуказывая наloaderContextизloader-runnerуникальный объект. Заинтересованные друзья могут прочитать исходный код самостоятельно.

module.exports = function(source) {
    const content = doSomeThing2JsString(source);
    
    // 如果 loader 配置了 options 对象,那么this.query将指向 options
    const options = this.query;
    
    // 可以用作解析其他模块路径的上下文
    console.log('this.context');
    
    /*
     * this.callback 参数:
     * error:Error | null,当 loader 出错时向外抛出一个 error
     * content:String | Buffer,经过 loader 编译后需要导出的内容
     * sourceMap:为方便调试生成的编译后内容的 source map
     * ast:本次编译生成的 AST 静态语法树,之后执行的 loader 可以直接使用这个 AST,进而省去重复生成 AST 的过程
     */
    this.callback(null, content);
    // or return content;
}

Более подробную документацию по разработке можно посмотреть прямо на официальном сайтеLoader API.

Вы написали плагин? Кратко опишите идею написания плагина?

если мы предположимLoaderотвечает за преобразование файлов, затемPluginОн отвечает за расширение функций.LoaderиPluginв видеWebpackДва важных компонента компании берут на себя две разные обязанности.

Выше было сказано,webpackНа основе модели публикации-подписки в течение рабочего жизненного цикла будет транслироваться множество событий, прослушивая эти события, плагин может выполнять свои собственные задачи плагина на определенном этапе, тем самым реализуя нужные функции.

Поскольку он основан на модели публикации-подписки, то знайтеWebpackКак упоминалось выше, очень важно, какие обработчики событий предоставляются разработчикам подключаемых модулей.compilerиcompilationдаWebpackДва очень важных объекта, из которыхcompilerподвергается иWebpackХуки, связанные со всем жизненным циклом (compiler-hooks),иcompilationзатем предоставляет более детализированные перехватчики событий, связанные с модулями и зависимостями (Compilation Hooks).

WebpackМеханизм события основан наwebpackсамореализуемый наборTapableСхема потоковой передачи событий (github)

// Tapable的简单使用
const { SyncHook } = require("tapable");

class Car {
    constructor() {
        // 在this.hooks中定义所有的钩子事件
        this.hooks = {
            accelerate: new SyncHook(["newSpeed"]),
            brake: new SyncHook(),
            calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"])
        };
    }

    /* ... */
}


const myCar = new Car();
// 通过调用tap方法即可增加一个消费者,订阅对应的钩子事件了
myCar.hooks.brake.tap("WarningLampPlugin", () => warningLamp.on());

Pluginразвитие и развитиеLoaderТочно так же необходимо соблюдать некоторые нормы и принципы развития:

  • Плагин должен быть функцией или включатьapplyобъект метода, чтобы вы могли получить доступcompilerпример;
  • передается каждому плагинуcompilerиcompilationВсе объекты являются одной и той же ссылкой, если свойства на них изменены в одном плагине, это повлияет на следующие плагины;
  • Асинхронные события должны вызывать уведомление функции обратного вызова, когда плагин завершает обработку задачи.WebpackВойдите в следующий процесс, иначе он зависнет;

Зная вышеизложенное, я хочу разработатьWebpack Plugin, это не сложно.

class MyPlugin {
  apply (compiler) {
    // 找到合适的事件钩子,实现自己的插件功能
    compiler.hooks.emit.tap('MyPlugin', compilation => {
        // compilation: 当前打包构建流程的上下文
        console.log(compilation);
        
        // do something...
    })
  }
}

Более подробную документацию по разработке можно посмотреть прямо на официальном сайтеPlugin API.

Наконец

Эта статья также сочетает в себе несколько отличных статей иwebpackИсходный код сам по себе примерно описывает несколько относительно важных концепций и процессов, а детали реализации и проектные идеи необходимо читать и медленно понимать вместе с исходным кодом.

WebpackЯвляясь отличным инструментом упаковки, он изменил традиционный режим разработки интерфейса и является краеугольным камнем современной разработки интерфейса. У такого превосходного проекта с открытым исходным кодом есть много отличных дизайнерских идей и концепций, на которых можно поучиться.Естественно, мы не должны останавливаться только наAPIНа уровне использования попытка прочитать исходный код с вопросами и понять процесс и принцип реализации также может позволить нам получить больше знаний и глубже понять, чтобы мы могли легко применить их в проекте.

Ссылки на связанную документацию

Официальный сайт вебпака

«Кровавая рвота» и еще дюжина вопросов интервью Webpack