Webpack Series 2: демистификация работы плагинов webpack
Webpack, серия 3: чтение исходного кода основного процесса webpack и реализация webpack
предисловие
С помощью плагинов мы можем расширитьwebpack
, проходя в нужное времяWebpack
Предоставленный API изменяет вывод так, чтоwebpack
Может выполнять более широкий спектр задач и иметь более сильные возможности сборки.
В этой статье мы попытаемся изучитьwebpack
Рабочий процесс плагина, а затем демистификация того, как он работает. Также нужно, чтобы выwebpack
И строить что-то лежащие в основе процессов есть определенное понимание.
Чтобы понять механизм плагинов webpack, вам необходимо понять следующие моменты:
- Состав простого плагина
-
webpack
процесс сборки -
Tapable
как соединить плагины между собой -
compiler
так же какcompilation
Использование объектов и соответствующих им обработчиков событий.
Базовая структура плагина
plugins
может использовать свой собственный метод-прототипapply
объект для создания экземпляра.apply
только после установки плагинаWebpack compiler
Выполнить один раз.apply
метод проходит черезwebpck compiler
ссылка для доступа к обратным вызовам компилятора.
Простая структура плагина:
class HelloPlugin{
// 在构造函数中获取用户给该插件传入的配置
constructor(options){
}
// Webpack 会调用 HelloPlugin 实例的 apply 方法给插件实例传入 compiler 对象
apply(compiler) {
// 在emit阶段插入钩子函数,用于特定时机处理额外的逻辑;
compiler.hooks.emit.tap('HelloPlugin', (compilation) => {
// 在功能流程完成后可以调用 webpack 提供的回调函数;
});
// 如果事件是异步的,会带两个参数,第二个参数为回调函数,在插件处理完任务时需要调用回调函数通知webpack,才会进入下一个处理流程。
compiler.plugin('emit',function(compilation, callback) {
// 支持处理逻辑
// 处理完毕后执行 callback 以通知 Webpack
// 如果不执行 callback,运行流程将会一直卡在这不往下执行
callback();
});
}
}
module.exports = HelloPlugin;
При установке плагина вам нужно всего лишь поместить его экземпляр вWebpack config plugins
Внутри массива:
const HelloPlugin = require('./hello-plugin.js');
var webpackConfig = {
plugins: [
new HelloPlugin({options: true})
]
};
Давайте сначала проанализируем принцип работы плагина webpack.
- Сначала будет выполнен процесс чтения конфигурации
new HelloPlugin(options)
инициализироватьHelloPlugin
получить его экземпляр. - инициализация
compiler
вызывается после объектаHelloPlugin.apply(compiler)
Передайте экземпляр плагинаcompiler
объект. - Экземпляр плагина получает
compiler
объект, вы можете пройтиcompiler.plugin(事件名称, 回调函数)
Слушайте события, транслируемые Webpack. и может пройтиcompiler
объект для работыWebpack
.
процесс сборки вебпака
Прежде чем писать плагин, нужно знать еще несколько вещей.Webpack
процесс сборки, чтобы вставить правильную логику плагина в нужное время.
Основной процесс сборки Webpack выглядит следующим образом:
- Проверьте файл конфигурации: прочитайте ввод командной строки или
webpack.config.js
файл для инициализации параметров конфигурации для этой сборки - генерировать
Compiler
Объект: выполняет оператор создания экземпляра плагина в файле конфигурации.new MyWebpackPlugin()
,дляwebpack
Поток событий зависает на пользовательскомhooks
- Войти
entryOption
сцена:webpack
начать чтение конфигурацииEntries
, рекурсивно просмотреть все входные файлы -
run/watch
: если работаетwatch
режим выполненwatch
метод, иначе выполнитьrun
метод -
compilation
:СоздайтеCompilation
обратный вызов объектаcompilation
Связанные хуки, вводите каждый входной файл по очереди (entry
), используйте загрузчик для компиляции файла. пройти черезcompilation
я могу читатьmodule
изresource
(путь ресурса),loaders
(загрузчик используется) и т. д. Затем используйте содержимое скомпилированного файлаacorn
Анализ для создания статического синтаксического дерева AST. Затем выполните этот процесс рекурсивно и многократно, После того, как все модули и зависимости проанализированы, выполнитеcompilation
изseal
Метод организует, оптимизирует и инкапсулирует каждый фрагмент__webpack_require__
для имитации модульных операций. -
emit
: Выполнена компиляция и конвертация всех файлов, включая финальные выходные ресурсы, мы можем отзвонить событие во входящем событииcompilation.assets
Получить необходимые данные, в том числе информацию о ресурсах для вывода, блоках кода Chunk и т.д.
// 修改或添加资源
compilation.assets['new-file.js'] = {
source() {
return 'var a=1';
},
size() {
return this.source().length;
}
};
-
afterEmit
: файл был полностью записан на диск -
done
: полная компиляция
Предложите часть блога Didi CloudWebPack
Скомпилируйте блок-схему, если вам не нравится читать текстовое объяснение, вы можете прочитать блок-схему, чтобы понять память
Блок-схема компиляции WebPackИсходное изображение из:blog.Brother Yoon.com/index.PHP/2…
После прочтения, если вы все еще не можете понять или понять процесс создания веб-пакета, рекомендуется прочитать полный текст, а затем вернуться к прочтению этого отрывка, я думаю, у вас будет более глубокое понимание процесса создания веб-пакета.
Понимание механизма потока событий Tapable
webpack
По сути, это механизм потока событий, и его рабочий процесс заключается в последовательном подключении различных подключаемых модулей, и ядром всего этого является Tapable.
Webpack
изTapable
Механизм потока событий обеспечивает упорядоченность плагинов, подключая каждый плагин последовательно. Webpack будет транслировать события во время запущенного процесса. Плагину нужно только прослушивать события, которые его интересуют, а затем его можно добавлен к этому механизму webapck для изменения webapck.Операция делает всю систему хорошо расширяемой.
Tapable
Также небольшая библиотека, даWebpack
основной инструмент. похожий наnode
серединаevents
Основным принципом библиотеки является модель публикации по подписке. Роль заключается в предоставлении аналогичного интерфейса подключаемого модуля.
Ядро webpack отвечает за компиляциюCompiler
и отвечает за создание пакетовCompilation
являются экземплярами Tapable, которые можно использовать непосредственно вCompiler
а такжеCompilation
Передайте и прослушайте события на объекте, методы следующие:
/**
* 广播事件
* event-name 为事件名称,注意不要和现有的事件重名
*/
compiler.apply('event-name',params);
compilation.apply('event-name',params);
/**
* 监听事件
*/
compiler.plugin('event-name',function(params){});
compilation.plugin('event-name', function(params){});
Tapable
класс выставленtap
,tapAsync
а такжеtapPromise
метод, вы можете выбрать логику внедрения функции в соответствии с синхронным/асинхронным режимом хука.
tap
Синхронизирующий хук
compiler.hooks.compile.tap('MyPlugin', params => {
console.log('以同步方式触及 compile 钩子。')
})
tapAsync
Асинхронные хуки черезcallback
обратный вызов сообщаетWebpack
Асинхронное выполнение завершеноtapPromise
Асинхронный хук, возвращаетPromise
РассказыватьWebpack
Асинхронное выполнение завершено
compiler.hooks.run.tapAsync('MyPlugin', (compiler, callback) => {
console.log('以异步方式触及 run 钩子。')
callback()
})
compiler.hooks.run.tapPromise('MyPlugin', compiler => {
return new Promise(resolve => setTimeout(resolve, 1000)).then(() => {
console.log('以具有延迟的异步方式触及 run 钩子')
})
})
Использование Tapable
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
Простая реализация SyncHook
class Hook{
constructor(args){
this.taps = []
this.interceptors = [] // 这个放在后面用
this._args = args
}
tap(name,fn){
this.taps.push({name,fn})
}
}
class SyncHook extends Hook{
call(name,fn){
try {
this.taps.forEach(tap => tap.fn(name))
fn(null,name)
} catch (error) {
fn(error)
}
}
}
tapable
как будетwebapck/webpack
Связано с плагином?
Compiler.js
const { AsyncSeriesHook ,SyncHook } = require("tapable");
//创建类
class Compiler {
constructor() {
this.hooks = {
run: new AsyncSeriesHook(["compiler"]), //异步钩子
compile: new SyncHook(["params"]),//同步钩子
};
},
run(){
//执行异步钩子
this.hooks.run.callAsync(this, err => {
this.compile(onCompiled);
});
},
compile(){
//执行同步钩子 并传参
this.hooks.compile.call(params);
}
}
module.exports = Compiler
MyPlugin.js
const Compiler = require('./Compiler')
class MyPlugin{
apply(compiler){//接受 compiler参数
compiler.hooks.run.tap("MyPlugin", () => console.log('开始编译...'));
compiler.hooks.complier.tapAsync('MyPlugin', (name, age) => {
setTimeout(() => {
console.log('编译中...')
}, 1000)
});
}
}
//这里类似于webpack.config.js的plugins配置
//向 plugins 属性传入 new 实例
const myPlugin = new MyPlugin();
const options = {
plugins: [myPlugin]
}
let compiler = new Compiler(options)
compiler.run()
хочу узнать большеtapable
В статье можно посмотреть эту статью:
webpack4
основной модульtapable
Анализ исходного кода:
https://www.cnblogs.com/tugenhua0707/p/11317557.html
Понимание компилятора (отвечает за компиляцию)
Первое, что нужно знать при разработке плагинаcompiler
а такжеcompilation
что делает объект
Compiler
объект содержит текущий запущенныйWebpack
конфигурация, в том числеentry、output、loaders
Дождитесь настройки, этот объект запускаетсяWebpack
создается и глобально уникален.Plugin
Информация о конфигурации Webpack может быть получена через этот объект для обработки.
Если вы читаете это, вы все еще не понимаетеcompiler
Что он делает, не бойтесь, продолжайте смотреть.
бегатьnpm run build
,Пучокcompiler
Вся информация выводится на консольconsole.log(Compiler)
.
// 为了能更直观的让大家看清楚compiler的结构,里面的大量代码使用省略号(...)代替。
Compiler {
_pluginCompat: SyncBailHook {
...
},
hooks: {
shouldEmit: SyncBailHook {
...
},
done: AsyncSeriesHook {
...
},
additionalPass: AsyncSeriesHook {
...
},
beforeRun: AsyncSeriesHook {
...
},
run: AsyncSeriesHook {
...
},
emit: AsyncSeriesHook {
...
},
assetEmitted: AsyncSeriesHook {
...
},
afterEmit: AsyncSeriesHook {
...
},
thisCompilation: SyncHook {
...
},
compilation: SyncHook {
...
},
normalModuleFactory: SyncHook {
...
},
contextModuleFactory: SyncHook {
...
},
beforeCompile: AsyncSeriesHook {
...
},
compile: SyncHook {
...
},
make: AsyncParallelHook {
...
},
afterCompile: AsyncSeriesHook {
...
},
watchRun: AsyncSeriesHook {
...
},
failed: SyncHook {
...
},
invalid: SyncHook {
...
},
watchClose: SyncHook {
...
},
infrastructureLog: SyncBailHook {
...
},
environment: SyncHook {
...
},
afterEnvironment: SyncHook {
...
},
afterPlugins: SyncHook {
...
},
afterResolvers: SyncHook {
...
},
entryOption: SyncBailHook {
...
},
infrastructurelog: SyncBailHook {
...
}
},
...
outputPath: '',//输出目录
outputFileSystem: NodeOutputFileSystem {
...
},
inputFileSystem: CachedInputFileSystem {
...
},
...
options: {
//Compiler对象包含了webpack的所有配置信息,entry、module、output、resolve等信息
entry: [
'babel-polyfill',
'/Users/frank/Desktop/fe/fe-blog/webpack-plugin/src/index.js'
],
devServer: { port: 3000 },
output: {
...
},
module: {
...
},
plugins: [ MyWebpackPlugin {} ],
mode: 'production',
context: '/Users/frank/Desktop/fe/fe-blog/webpack-plugin',
devtool: false,
...
performance: {
maxAssetSize: 250000,
maxEntrypointSize: 250000,
hints: 'warning'
},
optimization: {
...
},
resolve: {
...
},
resolveLoader: {
...
},
infrastructureLogging: { level: 'info', debug: false }
},
context: '/Users/frank/Desktop/fe/fe-blog/webpack-plugin',//上下文,文件目录
requestShortener: RequestShortener {
...
},
...
watchFileSystem: NodeWatchFileSystem {
//监听文件变化列表信息
...
}
}
Исходный код компилятора упрощает анализ кода версии
Адрес исходного кода (строка 948): https://github.com/webpack/webpack/blob/master/lib/Compiler.js.
const { SyncHook, SyncBailHook, AsyncSeriesHook } = require("tapable");
class Compiler {
constructor() {
// 1. 定义生命周期钩子
this.hooks = Object.freeze({
// ...只列举几个常用的常见钩子,更多hook就不列举了,有兴趣看源码
done: new AsyncSeriesHook(["stats"]),//一次编译完成后执行,回调参数:stats
beforeRun: new AsyncSeriesHook(["compiler"]),
run: new AsyncSeriesHook(["compiler"]),//在编译器开始读取记录前执行
emit: new AsyncSeriesHook(["compilation"]),//在生成文件到output目录之前执行,回调参数: compilation
afterEmit: new AsyncSeriesHook(["compilation"]),//在生成文件到output目录之后执行
compilation: new SyncHook(["compilation", "params"]),//在一次compilation创建后执行插件
beforeCompile: new AsyncSeriesHook(["params"]),
compile: new SyncHook(["params"]),//在一个新的compilation创建之前执行
make:new AsyncParallelHook(["compilation"]),//完成一次编译之前执行
afterCompile: new AsyncSeriesHook(["compilation"]),
watchRun: new AsyncSeriesHook(["compiler"]),
failed: new SyncHook(["error"]),
watchClose: new SyncHook([]),
afterPlugins: new SyncHook(["compiler"]),
entryOption: new SyncBailHook(["context", "entry"])
});
// ...省略代码
}
newCompilation() {
// 创建Compilation对象回调compilation相关钩子
const compilation = new Compilation(this);
//...一系列操作
this.hooks.compilation.call(compilation, params); //compilation对象创建完成
return compilation
}
watch() {
//如果运行在watch模式则执行watch方法,否则执行run方法
if (this.running) {
return handler(new ConcurrentCompilationError());
}
this.running = true;
this.watchMode = true;
return new Watching(this, watchOptions, handler);
}
run(callback) {
if (this.running) {
return callback(new ConcurrentCompilationError());
}
this.running = true;
process.nextTick(() => {
this.emitAssets(compilation, err => {
if (err) {
// 在编译和输出的流程中遇到异常时,会触发 failed 事件
this.hooks.failed.call(err)
};
if (compilation.hooks.needAdditionalPass.call()) {
// ...
// done:完成编译
this.hooks.done.callAsync(stats, err => {
// 创建compilation对象之前
this.compile(onCompiled);
});
}
this.emitRecords(err => {
this.hooks.done.callAsync(stats, err => {
});
});
});
});
this.hooks.beforeRun.callAsync(this, err => {
this.hooks.run.callAsync(this, err => {
this.readRecords(err => {
this.compile(onCompiled);
});
});
});
}
compile(callback) {
const params = this.newCompilationParams();
this.hooks.beforeCompile.callAsync(params, err => {
this.hooks.compile.call(params);
const compilation = this.newCompilation(params);
//触发make事件并调用addEntry,找到入口js,进行下一步
this.hooks.make.callAsync(compilation, err => {
process.nextTick(() => {
compilation.finish(err => {
// 封装构建结果(seal),逐次对每个module和chunk进行整理,每个chunk对应一个入口文件
compilation.seal(err => {
this.hooks.afterCompile.callAsync(compilation, err => {
// 异步的事件需要在插件处理完任务时调用回调函数通知 Webpack 进入下一个流程,
// 不然运行流程将会一直卡在这不往下执行
return callback(null, compilation);
});
});
});
});
});
});
}
emitAssets(compilation, callback) {
const emitFiles = (err) => {
//...省略一系列代码
// afterEmit:文件已经写入磁盘完成
this.hooks.afterEmit.callAsync(compilation, err => {
if (err) return callback(err);
return callback();
});
}
// emit 事件发生时,可以读取到最终输出的资源、代码块、模块及其依赖,并进行修改(这是最后一次修改最终文件的机会)
this.hooks.emit.callAsync(compilation, err => {
if (err) return callback(err);
outputPath = compilation.getPath(this.outputPath, {});
mkdirp(this.outputFileSystem, outputPath, emitFiles);
});
}
// ...省略代码
}
apply
Общая форма вставки хука в метод выглядит следующим образом:
// compiler提供了compiler.hooks,可以根据这些不同的时刻去让插件做不同的事情。
compiler.hooks.阶段.tap函数('插件名称', (阶段回调参数) => {
});
compiler.run(callback)
Понимание компиляции (отвечает за создание пакетов)
Compilation
Объект представляет сборку версии ресурса. при бегеwebpack
При разработке ПО промежуточного слоя всякий раз, когда обнаруживается изменение файла, создается новыйcompilation
, что приводит к новому набору скомпилированных ресурсов. ОдинCompilation
Объект представляет текущие ресурсы модуля, скомпилированные и сгенерированные ресурсы, измененные файлы и информацию о состоянии отслеживаемых зависимостей.Compilation
Объект также предоставляет обратные вызовы для подключаемых модулей, которым требуются пользовательские функции, чтобы подключаемые модули могли использовать расширения при выполнении пользовательской обработки.
Проще говоря,Compilation
Ответственность за создание модулей и чанков, а также за использование плагинов для оптимизации процесса сборки.
а такжеCompiler
То же использование, разные типы хуков, также доступные на некоторых хукахtapAsync
а такжеtapPromise。
консольный выводconsole.log(compilation)
пройти черезCompilation
также может читатьCompiler
объект.
Там более 2000 строк исходного кода, я больше не могу его читать -- -- Если вам интересно, можете сами глянуть. https://github.com/webpack/webpack/blob/master/lib/Compilation.js
Познакомить с несколькими часто используемыми хуками компиляции.
крюк | Типы | когда звонить |
---|---|---|
buildModule | SyncHook | Запускается перед началом компиляции модуля, может использоваться для модификации модуля |
succeedModule | SyncHook | Этот хук выполняется, когда модуль успешно скомпилирован |
finishModules | AsyncSeriesHook | Вызывается, когда все модули успешно скомпилированы |
seal | SyncHook | когда однаждыcompilation Запускается, когда вы перестаете получать новые модули |
optimizeDependencies | SyncBailHook | Выполнить в начале оптимизации зависимостей |
optimize | SyncHook | Выполняется в начале фазы оптимизации |
optimizeModules | SyncBailHook | Выполняется в начале фазы оптимизации модуля, плагин может выполнять оптимизацию модуля в этом хуке, параметры обратного вызова:modules
|
optimizeChunks | SyncBailHook | Выполняется в начале фазы оптимизации блока кода, плагин может выполнять оптимизацию блока кода в этом хуке, параметры обратного вызова:chunks
|
optimizeChunkAssets | AsyncSeriesHook | Оптимизируйте любые ресурсы блока кода, которые хранятся вcompilation.assets начальство. У чанка есть свойство files, которое указывает на все файлы, созданные чанком. Любые дополнительные ресурсы фрагмента хранятся вcompilation.additionalChunkAssets начальство. Параметры обратного вызова:chunks
|
optimizeAssets | AsyncSeriesHook | Оптимизировать все, что хранится вcompilation.assets всех ресурсов. Параметры обратного вызова:assets
|
Разница между компилятором и компиляцией
Compiler
представляет весьWebpack
жизненный цикл от запуска до закрытия, в то время какCompilation
Он просто представляет собой новую компиляцию, пока файл изменяется,compilation
будет воссоздан.
Общий API
Плагины можно использовать для изменения выходных файлов, добавления выходных файлов и даже улучшенияWebpack
производительность и т. д. Короче говоря, плагин вызываетWebpack
который предоставилAPI
Может добиться многого. из-заWebpack
который предоставилAPI
очень много, есть многоAPI
Редко используемые, плюс ограниченное пространство, вот некоторые часто используемые API.
Чтение выходных ресурсов, блоков кода, модулей и их зависимостей
Некоторые плагины, возможно, потребуется прочитатьWebpack
, такие как вывод ресурсов, блоков кода, модулей и их зависимостей для дальнейшей обработки.
Когда происходит событие emit, преобразование и сборка исходного файла завершены.Здесь вы можете прочитать окончательный выходной ресурс, блок кода, модуль и его зависимости, а также изменить содержимое выходного ресурса.
Код плагина следующий:
class Plugin {
apply(compiler) {
compiler.plugin('emit', function (compilation, callback) {
// compilation.chunks 存放所有代码块,是一个数组
compilation.chunks.forEach(function (chunk) {
// chunk 代表一个代码块
// 代码块由多个模块组成,通过 chunk.forEachModule 能读取组成代码块的每个模块
chunk.forEachModule(function (module) {
// module 代表一个模块
// module.fileDependencies 存放当前模块的所有依赖的文件路径,是一个数组
module.fileDependencies.forEach(function (filepath) {
});
});
// Webpack 会根据 Chunk 去生成输出的文件资源,每个 Chunk 都对应一个及其以上的输出文件
// 例如在 Chunk 中包含了 CSS 模块并且使用了 ExtractTextPlugin 时,
// 该 Chunk 就会生成 .js 和 .css 两个文件
chunk.files.forEach(function (filename) {
// compilation.assets 存放当前所有即将输出的资源
// 调用一个输出资源的 source() 方法能获取到输出资源的内容
let source = compilation.assets[filename].source();
});
});
// 这是一个异步事件,要记得调用 callback 通知 Webpack 本次事件监听处理结束。
// 如果忘记了调用 callback,Webpack 将一直卡在这里而不会往后执行。
callback();
})
}
}
Отслеживание изменений файлов
Webpack
Начиная с настроенного модуля входа, он по очереди находит все зависимые модули, при изменении модуля входа или зависимых от него модулей срабатывает новый.Compilation
.
При разработке плагинов часто необходимо знать, какой файл был изменен, что привело к появлению новогоCompilation
, вы можете использовать для этого следующий код:
// 当依赖的文件发生变化时会触发 watch-run 事件
compiler.hooks.watchRun.tap('MyPlugin', (watching, callback) => {
// 获取发生变化的文件列表
const changedFiles = watching.compiler.watchFileSystem.watcher.mtimes;
// changedFiles 格式为键值对,键为发生变化的文件路径。
if (changedFiles[filePath] !== undefined) {
// filePath 对应的文件发生了变化
}
callback();
});
по умолчаниюWebpack
На наличие изменений отслеживаются только запись и зависимые от нее модули.В некоторых случаях в проекте может потребоваться введение новых файлов, например введениеHTML
документ. из-заJavaScript
файл не будет импортированHTML
документ,Webpack
не будет контролироватьHTML
изменения файлов, редактированиеHTML
файл не будет перезапускать новыйCompilation
. контролироватьHTML
изменения файла, нам нужно поставитьHTML
Файл добавляется в список зависимостей, для чего можно использовать следующий код:
compiler.hooks.afterCompile.tap('MyPlugin', (compilation, callback) => {
// 把 HTML 文件添加到文件依赖列表,好让 Webpack 去监听 HTML 模块文件,在 HTML 模版文件发生变化时重新启动一次编译
compilation.fileDependencies.push(filePath);
callback();
});
Изменить выходной ресурс
В некоторых сценариях подключаемым модулям необходимо изменять, добавлять и удалять выходные ресурсы.Для этого необходимо отслеживатьemit
событие, потому чтоemit
Когда происходит событие, файлы, соответствующие преобразованию всех модулей и блоков кода, уже сгенерированы, и ресурсы для вывода вот-вот будут выведены, поэтому событие эммита — это последний раз, когда нужно изменить выходные ресурсы Webpack.
Все ресурсы, которые необходимо вывести, будут храниться вcompilation.assets
середина,compilation.assets
Это пара ключ-значение, ключ — это имя файла для вывода, а значение — это содержимое, соответствующее файлу.
настраиватьcompilation.assets
Код выглядит следующим образом:
// 设置名称为 fileName 的输出资源
compilation.assets[fileName] = {
// 返回文件内容
source: () => {
// fileContent 既可以是代表文本文件的字符串,也可以是代表二进制文件的 Buffer
return fileContent;
},
// 返回文件大小
size: () => {
return Buffer.byteLength(fileContent, 'utf8');
}
};
callback();
Определите, какие плагины использует webpack
// 判断当前配置使用使用了 ExtractTextPlugin,
// compiler 参数即为 Webpack 在 apply(compiler) 中传入的参数
function hasExtractTextPlugin(compiler) {
// 当前配置所有使用的插件列表
const plugins = compiler.options.plugins;
// 去 plugins 中寻找有没有 ExtractTextPlugin 的实例
return plugins.find(plugin=>plugin.__proto__.constructor === ExtractTextPlugin) != null;
}
Вышеупомянутые 4 метода взяты из статьи: [Обучающий плагин Webpack]: http://wushaobin.top/2019/03/15/webpackPlugin/
Управление предупреждениями и ошибками
проведите эксперимент, если выapply
Вставить в функциюthrow new Error("Message")
, что происходит, терминал печатаетUnhandled rejection Error: Message
. Затем выполнение веб-пакета прерывается.
Чтобы не повлиятьwebpack
Реализация, чтобы выдать пользователю предупреждение или сообщение об ошибке во время компиляции, вы должны использовать компиляцию.предупреждения и компиляция.ошибки.
compilation.warnings.push("warning");
compilation.errors.push("error");
Отображение демо-кода кейса в статье
Как отладить процесс упаковки веб-пакета или код плагина?
- В текущей папке проекта проекта webpack выполните командную строку:
node --inspect-brk ./node_modules/webpack/bin/webpack.js --inline --progress
Параметр --inspect-brk запускает узел в режиме отладки:
Терминал выдаст:
Debugger listening on ws://127.0.0.1:9229/1018c03f-7473-4d60-b62c-949a6404c81d
For help, see: https://nodejs.org/en/docs/inspector
- Google Chrome типа chrome://inspect/#devices
- Затем нажмите «Продолжить» в отладчике Chrome, и точка останова останется в точке останова отладчика, которую мы установили в плагине.