Посмотреть настоящий плагин Webpack

Webpack
Посмотреть настоящий плагин Webpack

Что такое вебпак? Что может вебпак? Как использовать вебпак? Слишком много вводных туториалов.Я буду много искать, и я не буду говорить больше.Кроме того,официальная документация webpack должна быть эталоном индустрии документации.Продвинутый уровень уровня продукта в среде очень подробный и легко понять, поэтому я не буду вдаваться в подробности здесь, бросьте одинссылка на документацию веб-пакета, вы можете играть в нее в соответствии с учебником в первую очередь.

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

Во-первых, нужно понять одну интересную вещь: механизм плагинов webpack — это скелет всего инструмента webpack, и сам webpack строится на основе этого механизма плагинов.

Это кажется неудобным, это должно исходить от веб-пакетаисходный кодКонкретная реализация веб-пакета может быть четко объяснена.На данный момент можно понять, что ядром веб-пакета является компилятор (компилятор), и этот компилятор также предоставляется в виде подключаемого модуля к подключаемой платформе веб-пакета. .

плагин веб-пакета

Во-первых, давайте просто и ясно поймем, что такое плагин веб-пакета, как описано в следующем коде:some-webpack-pluginЭто простой плагин для веб-пакетов, который фокусируется на конкретной задаче в процессе компиляции веб-пакета.

const webpack = require('webpack');
// 假设有这么一个 webpack plugin
const SomewebpackPlugin = require('some-webpack-plugin');
webpack({
    // ...
    plugins: [
        new SomewebpackPlugin({/* some plugin options */})
    ]
    // ...
});

Так что же можно назвать плагином веб-пакета? Полный плагин веб-пакета должен соответствовать следующим правилам и характеристикам:

  • является независимым модулем.
  • Внешнее воздействие функционального модуля JS.
  • Инъекция определена на прототипе функцииcompilerобъектapplyметод.
  • applyФункция должна иметь обработчик событий веб-пакета, смонтированный через объект компилятора. Текущий скомпилированный объект компиляции можно получить в обратном вызове обработчика. Если это плагин асинхронной компиляции, можно получить обратный вызов обратного вызова.
  • Завершите процесс пользовательской подкомпиляции и обработайте внутренние данные объекта компиляции.
  • Если плагин скомпилирован асинхронно, обратный вызов будет выполнен после завершения обработки данных.

Это описывает базовую форму плагина веб-пакета, и разница между каждым плагином почти только в自定义子编译流程На этом этапе вы можете глубоко понять конкретную форму плагина веб-пакета через код:

// 1、some-webpack-plugin.js 文件(独立模块)
// 2、模块对外暴露的 js 函数
function SomewebpackPlugin(pluginOpions) {
    this.options = pluginOptions;
}
// 3、原型定义一个 apply 函数,并注入了 compiler 对象
SomewebpackPlugin.prototype.apply = function (compiler) {
    // 4、挂载 webpack 事件钩子(这里挂载的是 emit 事件)
    compiler.plugin('emit', function (compilation, callback) {
        // ... 内部进行自定义的编译操作
        // 5、操作 compilation 对象的内部数据
        console.log(compilation);
        // 6、执行 callback 回调
        callback();
    });
};
// 暴露 js 函数
module.exports = SomewebpackPlugin;

компилятор и объект компиляции

Благодаря предварительному пониманию плагина webpack мы заметили, что в плагине Webpcak есть два объекта, один объект компилятора, а другой объект компиляции.На первый взгляд, эти два объекта нужно спутать.Компилятор и объекты компиляции Два основных объекта веб-пакета являются ключом к расширению функциональности веб-пакета. Чтобы облегчить понимание механизма подключаемого модуля веб-пакета в будущем, давайте сначала сосредоточимся на этих двух объектах.

объект компилятора

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

объект компиляции

Прежде всего, нам нужно понять, что такое скомпилированные ресурсы.Компилированные ресурсы представляют собой статическую карту управления ресурсами, сгенерированную вебпаком через конфигурацию (все хранится в памяти), а упакованный файл вебпака описывается в виде ключ-значение. Скомпилированные ресурсы представляют собой эту карту, состоящую из пар "ключ-значение". Скомпилированные ресурсы должны быть сгенерированы объектом компиляции.

Экземпляр компиляции наследуется от компилятора, а объект компиляции представляет собой единую версию процесса сборки и генерации скомпилированных ресурсов веб-пакета. При запуске промежуточного программного обеспечения среды разработки webpack каждый раз, когда обнаруживается изменение файла, будет создаваться новая компиляция, что приводит к новому набору скомпилированных ресурсов и новому объекту компиляции. Объект компиляции содержит当前的模块资源,编译生成资源,变化的文件,так же как被跟踪依赖的状态信息. Скомпилированный объект также предоставляет ряд обратных вызовов ключевых точек, которые подключаемый модуль может выбрать для использования при выполнении пользовательской обработки.

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

Если вам нужно больше узнать о компиляторе и объектах компиляции, вы можете передатьconsole.log(compilation)Способ просмотра содержимого содержащихся объектов, но если вы хотите более глубоко понять слова, просмотрите исходный код — это очень хороший способ, вы получите более глубокое понимание веб-пакета.

Механизм плагина webpack

Webpack предоставляет гибкие и мощные пользовательские функции API в виде плагинов. Используя плагины, мы можем добавить функциональность в webpack. Кроме того, webpack предоставляет хуки жизненного цикла для регистрации плагинов. В каждой точке жизненного цикла webpack запускает все зарегистрированные плагины и предоставляет текущую информацию о статусе компиляции webpack.

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

Tapable и Tapable экземпляры

Архитектура плагинов webpack в основном основана наTapableРеализовано, Tapable — это внутренняя библиотека команды проекта webpack, которая в основном абстрагирует набор подключаемых механизмов. Некоторые экземпляры Tapable в исходном коде веб-пакета наследуют или смешивают класс Tapable. Tapable позволяет нам добавлять и применять плагины к нашим модулям JavaScript. Он может быть унаследован или смешан с другими модулями. Он похож на класс NodeJS EventEmitter и фокусируется на запуске и действии пользовательских событий. В дополнение к этому, Tapable позволяет вам получить доступ к производителю события через аргументы функции обратного вызова.

Объекты экземпляра Tapable имеют четыре набора функций-членов:

  • plugin(name<string>, handler<function>)- Этот метод позволяет зарегистрировать пользовательский плагин для событий экземпляра Tapable. Эта операция аналогична операции EventEmitter.on(), 注册一个处理函数 -> 监听器到某个信号 -> 事件发生时执行(Плагины, определяемые разработчиком, должны часто использовать этот метод для настройки функции обработчика перехватчиков событий, чтобы они могли быть отправлены основным процессом компиляции.).

  • apply(...pluginInstances<AnyPlugin|function>[])— AnyPlugin — это подкласс AbstractPlugin, либо класс с методом apply (или, в редких случаях, объект), либо просто функция с регистрационным кодом. Этот метод просто применяет определение подключаемого модуля, поэтому фактический прослушиватель событий будет зарегистрирован в реестре экземпляра Tapable.

  • applyPlugins*(name<string>, ...)- Это набор функций, с помощью которых экземпляр Tapable может применяться ко всем плагинам под указанным хэшем. Эти методы работают аналогично EventEmitter.emit(), вы можете использовать разные стратегии для управления выпуском событий для разных вариантов использования (Во внутреннем механизме реализации webpack этот метод часто используется в процессе компиляции основного процесса для создания настраиваемых подключаемых модулей, настраиваемых перехватчиков событий внешних подключаемых модулей.).

  • mixin(pt<Object>)- Простой способ расширить прототип Tapable гибридным способом, а не наследовать.

TapableREADMEТакже подробно описано в , стоит отметить, что эта группаapplyPlugins*метод,*Регистрации событий, представляющие различные ситуации, этот наборapplyPlugins*Методы можно увидеть повсюду в исходном коде веб-пакета, они также включают порядок выполнения плагинов веб-пакета, разныеapplyPlugins*Соответствует следующим различным ситуациям:

  • Плагин синхронного последовательного выполнения -applyPlugins()
  • Выполнять плагины параллельно -applyPluginsParallel()
  • Плагины выполняются один за другим, и каждый плагин получает возвращаемое значение (водопад) предыдущего плагина —applyPluginsWaterfall()
  • Асинхронно выполнить плагин -applyPluginsAsync()
  • Защищенный режим завершает выполнение плагина: как только плагин возвращается非 undefined, завершит запущенный процесс и вернет возвращаемое значение этого плагина. Это похоже на EventEmitteronce(), но они совсем другие -applyPluginsBailResult()

Многие объекты в webpack расширяют класс Tapable, открываяpluginметод. можно использовать плагиныpluginметод для внедрения пользовательских шагов сборки. В различных плагинах веб-пакета вы можете увидетьcompiler.pluginа такжеcompilation.pluginиспользуется часто. По сути, каждый вызов плагина связан с обратным вызовом в процессе сборки для запуска определенного шага. Каждый плагин будет установлен один раз при запуске webpack, webpack вызвав плагинapplyдля их установки и передать ссылку на объект компилятора веб-пакета. Тогда вы можете позвонитьcompiler.pluginдля доступа к компиляции ресурсов и их отдельных шагов сборки.

Вот пример плагина webpack:

// MyPlugin.js
function MyPlugin(options) {
    // Configure your plugin with options...
}
MyPlugin.prototype.apply = function (compiler) {
    compiler.plugin('compile', function (params) {
        console.log('The compiler is starting to compile...');
    });
    compiler.plugin('compilation', function (compilation) {
        console.log('The compiler is starting a new compilation...');
        compilation.plugin('optimize', function () {
            console.log('The compilation is starting to optimize files...');
        });
    });
    // 异步的事件钩子
    compiler.plugin('emit', function (compilation, callback) {
        console.log('The compilation is going to emit files...');
        callback();
    });
};
module.exports = MyPlugin;

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

Note:

Читая исходный код веб-пакета, вы можете найти интересный дизайн.Ядром веб-пакета является объект компилятора веб-пакета, а сам объект компилятора является экземпляром Tapable. Объект компилятора отвечает за компиляцию объекта конфигурации веб-пакета и возврат экземпляра компиляции. Когда экземпляр Compilation запускается, он создает необходимый пакет (также известный как результат компиляции), что является фантастическим дизайном 👍.

работающий процесс webpack

Здесь нам нужно иметь глубокое понимание всего рабочего процесса веб-пакета.При написании подключаемого модуля веб-пакета нам нужно выполнять соответствующую обработку в нужное время, и все перехватчики событий, предоставляемые веб-пакетом, основаны на жизни цикл вебпака. Ниже приведена схема запущенного процесса webpack (мелкозернистый слишком сложен, webpack больше всего критикуется из-за его сложности, но и самый привлекательный еще и из-за своей сложности)

Изображение взято с Taobao FED:Таобао Fed.org/blog/2016/0…

Note:

Я рекомендую блог Taobao FED, который проясняет общий процесс веб-пакета, подробно объясняет базовую архитектуру веб-пакета и подробно анализирует компилятор и объекты компиляции.

webpack运行流程示意图

Щелкните для просмотра увеличенного изображения, рекомендуется сохранить локально

Перехватчики событий, связанные с плагинами webpack

Исходя из вышеизложенного, процесс пользовательской подкомпиляции в плагине веб-пакета должен взаимодействовать с основным процессом компиляции веб-пакета.Как мы можем гарантировать, что логика компиляции, определенная в нашем плагине, может работать точно в нужное время?

На самом деле, мы узнали о двух важных объектах webpack ранее, объекты компилятора и компиляции играют здесь важную роль.Мы также узнали, что эти два объекта являются экземплярами Tapable.Webpack использует унаследованные методы экземпляров Tapable, соответственно. обработчики событий регистрируются как в объекте компиляции, так и в объекте компиляции, что позволяет разработчикам вставлять свою собственную логику обработки в любой процесс компиляции веб-пакета.

Подход Webpack заключается в использовании экземпляров Tapable.applyPlugins*метод для предварительной установки этих перехватчиков событий. Конечно, веб-пакет также определяет некоторые внутренние или внешние перехватчики событий в некоторых других объектах экземпляра Tapable. Здесь мы в основном понимаем, какие события разделяют объект компилятора и объект компиляции, связанный с перехватчиком плагина.

ловушка событий компилятора

Чтобы позволить разработчикам легко писать плагины для веб-пакетов, официальный также предоставляет компилятору объектПерехватчики событий.

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

хук событий Время запуска получить контент Типы
entry-option инициализировать параметры - Синхронизировать
run начать компиляцию compiler асинхронный
compile Фактическая компиляция начинается до создания объекта компиляции. параметр компиляции Синхронизировать
compilation После того, как объект компиляции сгенерирован, вы можете управлять этим объектом. compilation Синхронизировать
make Рекурсивно анализировать зависимости от записи, готовой к сборке каждого модуля compilation параллельно
after-compile Процесс сборки компиляции завершается compliation асинхронный
emit Перед записью содержимого активов в памяти в папку на диске compilation асинхронный
after-emit После записи содержимого активов в памяти в папку на диске compilation асинхронный
done Завершить весь процесс компиляции stats Синхронизировать
failed когда компиляция не удалась error Синхронизировать

Как объект компилятора связывает обработчики событий? Принцип Tapable был представлен ранее, потому что сам webpack передавал различные ключевые точки в объекте компилятора, унаследованном от Tapable.applyPlugins*()Метод регистрирует хук события. Разработчику нужно только привязать событие. Компилятор выдаст событие, связанное разработчиком, в нужное время. Компилятор связывает хук события следующим образом:

// 前提是先要拿到 compiler 对象,apply 方法的回调中就能拿到,这里假设能拿到 compiler 对象
compiler.plugin('emit', function (compilation, callback) {
    // 可以得到 compilation 对象,如果是异步的事件钩子,能拿到 callback 回调。
    // 做一些异步的事情
    setTimeout(function () {
        console.log("Done with async work...");
        callback();
    }, 1000);
});

Хорошо видно, что обработчик событий компилятора основан на всем процессе компиляции, а гранулярность относительно грубая.Обычно, когда требуется мелкозернистая обработка результата компиляции, обработчик событий, определенный для объекта компиляции, незаменимый.

ловушка события компиляции

Объект компиляции был представлен ранее. Объект компиляции представляет собой процесс сборки и генерации скомпилированных ресурсов для одной версии веб-пакета. Объект компиляции имеет доступ ко всем модулям и их зависимостям (в основном циклическим зависимостям). На этапе компиляции модуль加载,封闭,优化,分块,哈希а также重建И так далее, это будет основное время жизни любой операции при компиляции.

Чтобы иметь дело с логикой на уровне модуля, очень необходимо понимать обработчики событий компиляции.Конечно, многие плагины веб-пакетов ловко используют эти перехватчики событий для выполнения большого количества невероятной работы. Каковы конкретные перехватчики событий компиляции? Давайте выберем некоторые из наиболее распространенных и важных для объяснения.официальная документация

normal-module-loader

Обыкновенный загрузчик модулей, который фактически загружает функции всех модулей в графе модулей (структура данных для всех модулей после анализа) один за другим.

Модули обычно называют модульными модулями, такими как AMD, CMD и т. д.

// 前提是能先取到 complation 对象(可以通过 compiler 事件钩子取到)
compilation.plugin('normal-module-loader', function (loaderContext, module) {
    // 这里是所有模块被加载的地方
    // 一个接一个,此时还没有依赖被创建,想拿到啥模块直接通过 module 取
});

seal

Начался закрытый компилятор, на этот раз он больше не получает никаких модулей и скомпилирован в закрытую фазу (см. блок-схему веб-пакета).

compilation.plugin('seal', function () {
    // 你已经不能再接收到任何模块
    // 回调没有参数
});

optimize

Оптимизируйте компиляцию, этот обработчик событий особенно важен, многие плагины будут основаны на этом обработчике событий, что указывает на то, что WebPack вступил в фазу оптимизации.

compilation.plugin('optimize', function () {
    // webpack 已经进入优化阶段
    // 回调没有参数
});

optimize-modules

оптимизация модуля

compilation.plugin('optimize-modules', function (modules) {
    // 等待处理的模块数组
    console.log(modules);
});

optimize-chunks

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

compilation.plugin('optimize-chunks', function (chunks) {
    //这里一般只有一个 chunk,除非你在配置中指定了多个入口
    chunks.forEach(function (chunk) {
        // chunk 含有模块的循环引用
        chunk.modules.forEach(function (module) {
            console.log(module);
            // module.loaders, module.rawRequest, module.dependencies 等。
        });
    });
});

additional-assets

Это асинхронный обработчик событий. На этом этапе для объекта компиляции могут быть созданы дополнительные ресурсы, что означает, что вы можете асинхронно добавить некоторые пользовательские ресурсы в конечный продукт. Вы можете посмотреть на добавление ресурса svg к активам. Пример:

compiler.plugin('compilation', function (compilation) {
    compilation.plugin('additional-assets', function (callback) {
        download('https://some.host/some/path/some.svg', function (resp) {
            if (resp.status === 200) {
                compilation.assets['webpack-version.svg'] = toAsset(resp);
                callback();
            }
            else {
                callback(new Error('[webpack-example-plugin] Unable to download the image'));
            }
        })
    });
});

optimize-chunk-assets

Оптимизируйте обработчик событий активов чанка. На этом этапе оптимизации можно изменить активы чанка, чтобы повторно изменить содержимое ресурса. Активы хранятся в this.assets, но не все они являются активами фрагментов. У чанка есть свойство files, которое идентифицирует все файлы, созданные этим чанком. Дополнительные активы хранятся в this.additionalChunkAssets.

Вот пример добавления заголовка комментария к каждому фрагменту:

compilation.plugin("optimize-chunk-assets", function (chunks, callback) {
    chunks.forEach(function (chunk) {
        chunk.files.forEach(function (file) {
            compilation.assets[file] = '/**some comments info**/\n' + compilation.assets[file];
        });
    });
    callback();
});

optimize-assets

Оптимизируйте асинхронные перехватчики событий всех ассетов.На этом этапе вы можете получать все ассеты напрямую через this.assets и выполнять пользовательские операции. аналогичныйoptimize-chunk-assetsНо этот обратный вызов события не может зацепить куски.

compilation.plugin("optimize-assets", function (asstes, callback) {
    console.log(assets);
    // 可以直接操作 assets 里面的 file
    callback();
});

Объект компиляции имеет некоторые другие обработчики событий, которые можно прочитать напрямую.официальная документация по веб-пакету, документация не очень подробная, лучше всего попробовать эти обработчики событий и поместить их в кодconsole.log()Запустите прогон и посмотрите конкретную реализацию и результаты, чтобы углубить свое понимание.

напиши в конце

Чтобы понять механику плагинов webpack, вам нужно понять несколько вещей:

  • форма плагина webpack
  • работающий процесс webpack
  • Tapable & Tapable Instances и Tapable Instances Methods
  • объекты компилятора и компиляции и обработчики событий

Остальное — выяснить ваши потребности и написать соответствующий плагин. Встроенные подключаемые модули веб-пакета и исходный код многих отличных подключаемых модулей веб-пакетов с открытым исходным кодом являются хорошими примерами обучения.

использованная литература

Эта статья является оригинальной статьей, и точки знаний будут часто обновляться, а некоторые ошибки будут исправлены. Поэтому, пожалуйста, сохраняйте исходный источник для перепечатки, чтобы облегчить отслеживание, избежать введения в заблуждение старых неправильных знаний и получить лучший опыт чтения .
При воспроизведении указать источник:Перейти Miaojiang.com/article/I ha…