🔥Разработка плагинов Webpack — это так просто!

внешний интерфейс Webpack
🔥Разработка плагинов Webpack — это так просто!

используется в этой статьеWebpack-Quickly-StarterБыстро создайте локальную среду обучения Webpack4.
Рекомендуется прочитать больше документации Webpack "Writing a Plugin", научитесь разрабатывать простые подключаемые модули.

Эта статья поможет вам разработать свой первый плагин Webpack от инженера по настройке Webpack до инженера-разработчика Webpack!
Сделайте свои собственные колеса и позвольте другим использовать их.

Полный код хранится в:GitHub.com/Ping An8787/…

1. Введение

Эта статья вдохновлена ​​​​обобщением опыта в бизнесе,Не боится богоподобных продуктов, боится только развития одного сухожилия.

Есть проблема с упаковкой проекта: «Когда проект размещается на платформе CDN, есть надежда, что index.js в проекте не будет кэшироваться». потому что нам нужно изменитьindex.jsСодержимое в , не хотите, чтобы пользователи кэшировались.

Поразмыслив некоторое время, появилось несколько идей:

  1. Отфильтруйте настройки кеша для этого файла на платформе CDN;
  2. Найдите элемент DOM, изменитеscriptпомеченsrcзначение и добавьте отметку времени;
  3. Динамически создается при упаковкеscriptВ файл импортируются теги и добавляются временные метки.

(Вы умны, и есть другие способы, добро пожаловать на обсуждение)

Анализ мыслей:

  1. Очевидно, что если вы измените настройки CDN, симптомы не исчезнут;
  2. В файле шаблона добавьтеscriptтег, выполните, чтобы получить те, которые автоматически добавляются Webpackscriptнаклей и сделайsrcЗначение добавляет метку времени. Но дело в том, что файл js был загружен до того, как вы закончили его модифицировать, так что сдавайтесь.
  3. нуждаться вindex.htmlПеред созданием измените путь к файлу js и добавьте метку времени.

Поэтому я собираюсь использовать третий метод, вindex.htmlПеред созданием выполните следующие изменения:
image.png

Проблема проста, но я все еще хочу попробовать разработать плагин Webpack.

2. Базовые знания

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

2.1 Состав плагина Webpack

Прежде чем настраивать плагин, нам нужно понять, что представляет собой плагин Webpack.Ниже приведен отрывок из документа:

  • именованная функция JavaScript;
  • определить метод применения на его прототипе;
  • Указать тач самому Webpackхук событий;
  • Манипулировать данными конкретного экземпляра внутри Webpack;
  • Обратный вызов, предоставляемый Webpack, вызывается после реализации функции. 

2.2 Базовая архитектура плагина Webpack

Плагины создаются конструктором. Определение конструктораapplyметод, при установке плагина,applyметод будет использоваться WebpackcompilerЗвонил один раз.applyметод может получить Webpackcompiler Ссылка на объект, к которому можно получить доступ в функции обратного вызоваcompilerобъект.

Официальная документация предоставляет простую структуру плагина:

class HelloWorldPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('Hello World Plugin', (
      stats /* 在 hook 被触及时,会将 stats 作为参数传入。 */
    ) => {
      console.log('Hello World!');
    });
  }
}
module.exports = HelloWorldPlugin;

Используйте плагин:

// webpack.config.js
var HelloWorldPlugin = require('hello-world');

module.exports = {
  // ... 这里是其他配置 ...
  plugins: [new HelloWorldPlugin({ options: true })]
};

2.3 Введение в HtmlWebpackPlugin

Плагин HtmlWebpackPlugin упрощает создание HTML-файлов для обслуживания пакетов Webpack. Это особенно полезно для пакетов веб-пакетов, в именах файлов которых есть хэши, которые меняются при каждой компиляции.

Основные функции плагина резюмируются:Создать HTML-файл.

html-webapck-pluginплагинДве главные роли:

  • Импортируйте внешние ресурсы (например,script / link) Динамически добавлять хэш после каждой компиляции, чтобы предотвратить проблему кэширования файлов, на которые ссылаются;
  • Динамически создавать файлы ввода HTML, например, для одностраничных приложений.index.html документ.

html-webapck-pluginплагинПринцип введения:

  • Читать в вебпакеentryЗапись, связанная с конфигурациейchunkа такжеextract-text-webpack-pluginстили CSS, извлекаемые плагинами;
  • Вставьте стили в предоставленный плагинtemplateилиtemplateContentВ файле шаблона, заданном конфигурацией;
  • Способ вставки: черезlinkтеги импортируют стили черезscriptЭтикетки импортируют файлы скриптов;

3. Процесс разработки

Разработано в этой статьеАвтоматически добавлять временные метки к ссылкам на файлы сценариев (SetScriptTimestampPlugin)Принцип реализации плагина: черезHtmlWebpackPluginПеред созданием файла HTML скопируйте файл шаблонаЗамените зарезервированное пространство скриптом, сценарий выполняет автоматическую отметку времени для ссылки на файл сценария.

3.1 Вставной рабочий механизм

SetScriptTimestampPlugin 运行机制.png

3.2 Инициализируйте файл плагина

новыйSetScriptTimestampPlugin.jsфайл и обратитесь к базовой структуре плагина в официальной документации для инициализации кода плагина:

// SetScriptTimestampPlugin.js

class SetScriptTimestampPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('SetScriptTimestampPlugin',
     (compilation, callback) => {
      console.log('SetScriptTimestampPlugin!');
    });
  }
}
module.exports = SetScriptTimestampPlugin;

applyМетод является подключаемым методом-прототипом, получаяcompilerкак параметр.

3.3 Выберите время запуска плагина

Выбор момента срабатывания подключаемого модуля на самом деле означает выбор хука компилятора, запускаемого подключаемым модулем (то есть, когда запускается подключаемый модуль).
Webpack предоставляет множество хуков. Вот некоторые из них вкратце. Полную информацию см. в документе "Compiler Hooks":

  • entryOption: в параметрах веб-пакетаentryПосле обработки элемента конфигурации плагин выполняется.
  • afterPlugins: После установки начального плагина запустите плагин.
  • compilation: После компиляции и создания перед генерацией файла выполните плагин. .
  • emit: генерировать ресурсы дляoutputперед каталогом.
  • done: Компиляция завершена.

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

// SetScriptTimestampPlugin.js

class SetScriptTimestampPlugin {
  apply(compiler) {
-   compiler.hooks.done.tap('SetScriptTimestampPlugin',
+   compiler.hooks.compilation.tap('SetScriptTimestampPlugin', 
      (compilation, callback) => {
      console.log('SetScriptTimestampPlugin!');
    });
  }
}
module.exports = SetScriptTimestampPlugin;

существуетcompiler.hooksуказанный вфункция ловушки событий, функция обратного вызова будет выполняться при срабатывании хука.
Webpack предоставляет три способа запуска хуков:

  • tapСинхронноспусковой крючок;
  • tapAsyncАсинхронный способспусковой крючок;
  • tapPromiseАсинхронный способАктивировать крючок и вернуть обещание;

Методы перехвата, которые могут быть выбраны этими тремя методами, также различны, потому чтоcompilation ДаSyncHookХук синхронизации, поэтому используйтеtapтриггерный метод.
tapМетод принимает два параметра: имя плагина и функцию обратного вызова.

3.4 Добавить запись о замене плагина

В принципе, мы указываем запись замены в файле шаблона, а затем заменяем ее скриптом, который необходимо выполнить.

image.png

Итак, мы в файле шаблонаtemplate.htmlдобавлено в<!--SetScriptTimestampPlugin inset script-->Замените запись как идентификатор:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Webpack 插件开发入门</title>
</head>
<body>
  	<!-- other code -->
    <!--SetScriptTimestampPlugin inset script-->
</body>
</html>

3.5 Написание логики плагина

На этом этапе мы начинаем писать логику плагина.
Из предыдущего шага мы знаем, что вtapВторой параметр — это функция обратного вызова, и эта функция обратного вызова имеет два параметра:compilation а такжеcallback.

compilationунаследовано отcompiler,ВключатьcompilerВсе (также Webpackoptions), а также имеетpluginТочка задачи доступа к функциям.

// SetScriptTimestampPlugin.js

class SetScriptTimestampPlugin {
  apply(compiler) {
    compiler.hooks.compilation.tap('SetScriptTimestampPlugin', 
      (compilation, callback) => {
      	// 插件逻辑 调用compilation提供的plugin方法
        compilation.plugin(
          "html-webpack-plugin-before-html-processing",
          function(htmlPluginData, callback) {
            // 读取并修改 script 上 src 列表
            let jsScr = htmlPluginData.assets.js[0];
            htmlPluginData.assets.js = [];
            let result = `
                <script>
                    let scriptDOM = document.createElement("script");
                    let jsScr = "./${jsScr}";
                    scriptDOM.src = jsScr + "?" + new Date().getTime();
                    document.body.appendChild(scriptDOM)
                </script>
            `;
            let resultHTML = htmlPluginData.html.replace(
              "<!--SetScriptTimestampPlugin inset script-->", result
            );
            // 返回修改后的结果
            htmlPluginData.html = resultHTML;
          }
        );
      }
    );
  }
}
module.exports = SetScriptTimestampPlugin;

В приведенной выше логике плагина эти вещи выполняются специально:

  1. воплощать в жизньcompilation.pluginметод и передать два параметра: событие плагина и метод обратного вызова.

Так называемые «события подключаемого модуля» — это некоторые события, предоставляемые подключаемым модулем для отслеживания состояния подключаемого модуля. Вот некоторые из них.html-webpack-pluginПредоставленные мероприятия (подробности см.html-webpack-plugin"):
Async:

  • html-webpack-plugin-before-html-generation
  • html-webpack-plugin-before-html-processing
  • html-webpack-plugin-alter-asset-tags

Sync:

  • html-webpack-plugin-alter-chunks
  1. Получите список имен файлов сценариев и очистите их.

В методе обратного вызова передайтеhtmlPluginData.assets.jsпройти черезscriptСписок имен импортированных файлов сценариев, сделайте копию и очистите исходный список.

image.png

  1. Напишите логику замены.

Логика замены такова: динамически создаватьscriptлейбл, поставьsrcВ качестве значения устанавливается имя файла скрипта, прочитанное на предыдущем шаге, и сплайсированное послеотметка временикак параметр.

  1. Вставьте логику замены.

пройти черезhtmlPluginData.htmlВы можете получить строковый вывод файла шаблона, нам просто нужно заменить запись в строке шаблона<!--SetScriptTimestampPlugin inset script-->Просто замените его логикой замены, которую мы написали на предыдущем шаге.

  1. Возвращает HTML-файл.

Наконец, назначьте измененную строку HTML исходномуhtmlPluginData.htmlдобиться эффекта модификации.

3.5 Использование плагинов

Пользовательское использование плагина, совместимое с другими плагинами, вpluginsСоздать экземпляр в массиве:

// webpack.config.js

const SetScriptTimestampPlugin = require("./SetScriptTimestampPlugin.js");
module.exports = {
	// ... 省略其他配置
  plugins: [
  	// ... 省略其他插件
    new SetScriptTimestampPlugin()  
  ]
}

На данный момент мы реализовали требование «Когда проект размещается на платформе CDN, мы надеемся, что index.js в проекте не будет кэшироваться».
image.png

4. Развитие дела

здесь раньшеSetScriptTimestampPluginПлагины, например, продолжают расширяться.

4.1 Чтение параметров конфигурации плагина

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

// SetScriptTimestampPlugin.js

class SetScriptTimestampPlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    console.log(this.options.filename); // "index.js"
    // ... 省略其他代码
  }
}
module.exports = SetScriptTimestampPlugin;

при его использовании:

// webpack.config.js

const SetScriptTimestampPlugin = require("./SetScriptTimestampPlugin.js");
module.exports = {
	// ... 省略其他配置
  plugins: [
  	// ... 省略其他插件
    new SetScriptTimestampPlugin({
    	filename: "index.js"
    })  
  ]
}

4.2 Добавление временных меток для файлов с несколькими сценариями

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

// webpack.config.js

const SetScriptTimestampPlugin = require("./SetScriptTimestampPlugin.js");
module.exports = {
	// ... 省略其他配置
  plugins: [
  	// ... 省略其他插件
    new SetScriptTimestampPlugin({
    	filename: ["index.js", "boundle.js", "pingan.js"]
    })  
  ]
}

Сгенерировать результат:

<script src="./index.js?1582425467655"></script>
<script src="./boundle.js?1582425467655"></script>
<script src="./pingan.js?1582425467655"></script>

V. Резюме

В этой статье используется общий настраиваемый плагин Webpack для выполнения некоторых из более сложных ежедневных требований. Он в основном знакомит с базовой композицией и простой архитектурой подключаемого модуля Webpack, а также знакомит с подключаемым модулем HtmlWebpackPlugin. Благодаря этим базовым знаниям подключаемый модуль замены текста HTML завершен, и, наконец, сфера использования подключаемого модуля расширяется за счет двух сценариев.

Наконец, есть больше знаний, чтобы узнать о разработке плагинов Webpack.Рекомендуется взглянуть на официальную документацию "Writing a Plugin" учиться.

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

Справочная документация

  1. "Writing a Plugin
  2. "HtmlWebpackPlugin - Webpack
  3. "Расширьте HtmlwebpackPlugin для вставки пользовательских скриптов.