webpack插件

plugin

插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以引入它们自己的行为到 webpack 构建流程中。创建插件比创建 loader 更加高级,因为你将需要理解一些 webpack 底层的内部特性来做相应的钩子

为什么需要一个插件

  • webpack 基础配置无法满足需求
  • 插件几乎能够任意更改 webpack 编译结果
  • webpack 内部也是通过大量内部插件实现的

可以加载插件的常用对象

对象钩子
Compilerrun,compile,compilation,make,emit,done
CompilationbuildModule,normalModuleLoader,succeedModule,finishModules,seal,optimize,after-seal
Module FactorybeforeResolver,afterResolver,module,parser
Module
Parserprogram,statement,call,expression
Templatehash,bootstrap,localVars,render

创建插件

  • 插件是一个类
  • 类上有一个apply的实例方法
  • apply的参数是compiler
class DonePlugin {constructor(options) {this.options = options;}apply(compiler) {}
}
module.exports = DonePlugin;

Compiler 和 Compilation

在插件开发中最重要的两个资源就是compilercompilation对象。理解它们的角色是扩展 webpack 引擎重要的第一步。

  • compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。
  • compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

基本插件架构

  • 插件是由「具有 apply 方法的 prototype 对象」所实例化出来的
  • 这个 apply 方法在安装插件时,会被 webpack compiler 调用一次
  • apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象

使用插件代码

  • [使用插件]https://github.com/webpack/webpack/blob/master/lib/webpack.js#L60-L69)
if (options.plugins && Array.isArray(options.plugins)) {for (const plugin of options.plugins) {plugin.apply(compiler);}
}

Compiler 插件

  • [done: new AsyncSeriesHook(“stats”])

同步

class DonePlugin {constructor(options) {this.options = options;}apply(compiler) {compiler.hooks.done.tap("DonePlugin", (stats) => {console.log("Hello ", this.options.name);});}
}
module.exports = DonePlugin;

异步

class DonePlugin {constructor(options) {this.options = options;}apply(compiler) {compiler.hooks.done.tapAsync("DonePlugin", (stats, callback) => {console.log("Hello ", this.options.name);callback();});}
}
module.exports = DonePlugin;

使用插件

  • 要安装这个插件,只需要在你的 webpack 配置的 plugin 数组中添加一个实例
const DonePlugin = require("./plugins/DonePlugin");
module.exports = {entry: "./src/index.js",output: {path: path.resolve("build"),filename: "bundle.js",},plugins: [new DonePlugin({ name: "hs" })],
};

compilation 插件

  • 使用 compiler 对象时,你可以绑定提供了编译 compilation 引用的回调函数,然后拿到每次新的 compilation 对象。这些 compilation 对象提供了一些钩子函数,来钩入到构建流程的很多步骤中

webpack-assets-plugin.js

plugins\webpack-assets-plugin.js

//编写个Compilation插件,用来打印本次产出的代码块和文件
class WebpackAssetsPlugin {constructor(options) {this.options = options;}apply(compiler) {//每当webpack开启一次新的编译 ,就会创建一个新的compilationcompiler.hooks.compilation.tap('WebpackAssetsPlugin', (compilation) => {//每次根据chunk创建一个新的文件后会触发一次chunkAssetcompilation.hooks.chunkAsset.tap('WebpackAssetsPlugin', (chunk, filename) => {console.log(chunk.name || chunk.id, filename);});});}
}
module.exports = WebpackAssetsPlugin;

打包 zip

  • webpack-sources

webpack-archive-plugin.js

plugins\webpack-archive-plugin.js

const jszip = require('jszip');
const { RawSource } = require('webpack-sources');
const { Compilation } = require('webpack');
/*** 1.如何获取打出后的文件名和文件内容* 2.如何打压缩包 * 3.如何向目标目录输出压缩包*/
class WebpackArchivePlugin {constructor(options) {this.options = options;}apply(compiler) {compiler.hooks.compilation.tap('WebpackAssetsPlugin', (compilation) => {//当确定好文件,当你处理每个资源的时候处执行compilation.hooks.processAssets.tapPromise({ name: 'WebpackArchivePlugin' }, (assets) => {const zip = new jszip();for (const filename in assets) {const sourceObj = assets[filename];const sourceCode = sourceObj.source();zip.file(filename, sourceCode);}return zip.generateAsync({ type: 'nodebuffer' }).then(zipContent => {assets[`archive_${Date.now()}。zip`] = new RawSource(zipContent);});});});}
}
module.exports = WebpackArchivePlugin;

webpack.config.js

webpack.config.js

const WebpackArchivePlugin = require('./plugins/webpack-archive-plugin');plugins: [
+   new WebpackArchivePlugin({
+     filename:'[timestamp].zip'
+   })
]

自动外链

自动外链

使用外部类库

  • 手动指定 external
  • 手动引入 script

能否检测代码中的 import 自动处理这个步骤?

{externals:{//key jquery是要require或import 的模块名,值 jQuery是一个全局变量名'jquery':'$'
}, module:{}
}

思路

  • 解决 import 自动处理externalscript的问题,需要怎么实现,该从哪方面开始考虑
  • 依赖 当检测到有importlibrary时,将其设置为不打包类似exteral,并在指定模版中加入 script,那么如何检测 import?这里就用Parser
  • external依赖 需要了解 external 是如何实现的,webpack 的 external 是通过插件ExternalsPlugin实现的,ExternalsPlugin 通过tap NormalModuleFactory 在每次创建 Module 的时候判断是否是ExternalModule
  • webpack4 加入了模块类型之后,Parser获取需要指定类型 moduleType,一般使用javascript/auto即可

使用 plugins

plugins: [new HtmlWebpackPlugin({template:'./src/index.html'}),new AutoExternalPlugin({jquery:{//自动把jquery模块变成一个外部依赖模块variable:'jQuery',//不再打包,而是从window.jQuery变量上获取jquery对象url:'https://cdn.bootcss.com/jquery/3.1.0/jquery.js'//CDN脚本},lodash:{//自动把jquery模块变成一个外部依赖模块variable:'_',//不再打包,而是从window.jQuery变量上获取jquery对象url:'https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js'//CDN脚本}})
];

AutoExternalPlugin

  • ExternalsPlugin.js
  • ExternalModuleFactoryPlugin
  • ExternalModule.js
  • parser
  • factory
  • htmlWebpackPluginAlterAssetTags

AsyncSeriesBailHook factorize

let { AsyncSeriesBailHook } = require("tapable");
let factorize = new AsyncSeriesBailHook(['resolveData']);
//最后总得返回一个模块吧 switch case default 
factorize.tapAsync('factory1', (resolveData, callback) => {if (resolveData === 'jquery') {callback(null, {id: resolveData,type: '外部模块',source: 'window.jQuery'});} else {callback(null);}
});
//生成正常模块的工厂函数最后一个工厂函数了,
factorize.tapAsync('factory2', (resolveData, callback) => {callback(null, { id: resolveData, type: '正常模块', source: 'webpack打包后的内容' });
});factorize.callAsync('jquery', (err, module) => {console.log(module);
});
factorize.callAsync('lodash', (err, module) => {console.log(module);
});

plugins\auto-external-plugin.js

const { ExternalModule } = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
/*** 1.需要向输出html文件中添加CDN脚本引用* 2.在打包生产模块的时候,截断正常的打包逻辑,变成外部依赖模块*/
class AutoExternalPlugin{constructor(options){this.options = options;this.externalModules = Object.keys(this.options);//['jquery'] 进行自动外键的模块this.importedModules = new Set();//存放着所有的实际真正使用到的外部依赖模块}apply(compiler){//每种模块会对应一个模块工厂 普通模块对应的就是普通模块工厂//https://webpack.docschina.org/api/normalmodulefactory-hooks/compiler.hooks.normalModuleFactory.tap('AutoExternalPlugin',(normalModuleFactory)=>{//https://webpack.docschina.org/api/parser/#rootnormalModuleFactory.hooks.parser.for('javascript/auto')//普通 的JS文件对应的钩子就是'javascript/auto'.tap('AutoExternalPlugin',parser=>{//在parser遍历语法的过程中,如果遍历到了import节点//https://webpack.docschina.org/api/parser/#importparser.hooks.import.tap('AutoExternalPlugin',(statement,source)=>{if(this.externalModules.includes(source)){this.importedModules.add(source);}});//https://webpack.docschina.org/api/parser/#call//call=HookMap key方法名 值是这个方法对应的钩子parser.hooks.call.for('require').tap('AutoExternalPlugin',(expression)=>{let value = expression.arguments[0].value;if(this.externalModules.includes(value)){this.importedModules.add(value);}});})//2.改造模块的生产过程,如果是外链模块,就直接生产一个外链模块返回//https://webpack.docschina.org/api/normalmodulefactory-hooks/normalModuleFactory.hooks.factorize.tapAsync('AutoExternalPlugin',(resolveData,callback)=>{let {request} = resolveData;//模块名 jquery lodashif(this.externalModules.includes(request)){let {variable} = this.options[request];//request=jquery window.jQuerycallback(null,new ExternalModule(variable,'window',request));}else{callback(null);//如果是正常模块,直接向后执行。走正常的打包模块的流程}});});//是往输出的HTML中添加一个新的CDN Script标签compiler.hooks.compilation.tap('AutoExternalPlugin',(compilation)=>{HtmlWebpackPlugin.getHooks(compilation).alterAssetTags.tapAsync('AutoExternalPlugin',(htmlData,callback)=>{//console.log(JSON.stringify(htmlData,null,2));Reflect.ownKeys(this.options).filter(key=>this.importedModules.has(key)).forEach(key=>{//jqueryhtmlData.assetTags.scripts.unshift({tagName:'script',voidTag:false,attributes:{defer:false,src:this.options[key].url}});})callback(null,htmlData);});});}
}
module.exports = AutoExternalPlugin;
/*** Node {type: 'ImportDeclaration',start: 0,end: 23,loc: SourceLocation {start: Position { line: 1, column: 0 },end: Position { line: 1, column: 23 }},range: [ 0, 23 ],specifiers: [Node {type: 'ImportDefaultSpecifier',start: 7,end: 8,loc: [SourceLocation],range: [Array],local: [Node]}],source: Node {type: 'Literal',start: 14,end: 22,loc: SourceLocation { start: [Position], end: [Position] },range: [ 14, 22 ],value: 'jquery',raw: "'jquery'"}
}
jquery*/

AsyncQueue

AsyncQueue

let AsyncQueue = require('webpack/lib/util/AsyncQueue');
let AsyncQueue = require('./AsyncQueue');
function processor(item, callback) {setTimeout(() => {console.log('process',item);callback(null, item);}, 3000);
}
const getKey = (item) => {return item.key;
}
let queue  = new AsyncQueue({name:'createModule',parallelism:3,processor,getKey
});
const start = Date.now();
let item1 = {key:'module1'};
queue.add(item1,(err,result)=>{console.log(err,result);console.log(Date.now() - start);
});
queue.add(item1,(err,result)=>{console.log(err,result);console.log(Date.now() - start);
});
queue.add({key:'module2'},(err,result)=>{console.log(err,result);console.log(Date.now() - start);
});
queue.add({key:'module3'},(err,result)=>{console.log(err,result);console.log(Date.now() - start);
});
queue.add({key:'module4'},(err,result)=>{console.log(err,result);console.log(Date.now() - start);
});

use.js

use.js

const QUEUED_STATE = 0;//已经 入队,待执行
const PROCESSING_STATE = 1;//处理中
const DONE_STATE = 2;//处理完成
class ArrayQueue {constructor() {this._list = [];}enqueue(item) {this._list.push(item);//[1,2,3]}dequeue() {return this._list.shift();//移除并返回数组中的第一个元素}
}
class AsyncQueueEntry {constructor(item, callback) {this.item = item;//任务的描述this.state = QUEUED_STATE;//这个条目当前的状态this.callback = callback;//任务完成的回调}
}
class AsyncQueue {constructor({ name, parallelism, processor, getKey }) {this._name = name;//队列的名字this._parallelism = parallelism;//并发执行的任务数this._processor = processor;//针对队列中的每个条目执行什么操作this._getKey = getKey;//函数,返回一个key用来唯一标识每个元素this._entries = new Map();this._queued = new ArrayQueue();//将要执行的任务数组队列 this._activeTasks = 0;//当前正在执行的数,默认值1this._willEnsureProcessing = false;//是否将要开始处理}add = (item, callback) => {const key = this._getKey(item);//获取这个条目对应的keyconst entry = this._entries.get(key);//获取 这个key对应的老的条目if (entry !== undefined) {if (entry.state === DONE_STATE) {process.nextTick(() => callback(entry.error, entry.result));} else if (entry.callbacks === undefined) {entry.callbacks = [callback];} else {entry.callbacks.push(callback);}return;}const newEntry = new AsyncQueueEntry(item, callback);//创建一个新的条目this._entries.set(key, newEntry);//放到_entriesthis._queued.enqueue(newEntry);//把这个新条目放放队列if (this._willEnsureProcessing === false) {this._willEnsureProcessing = true;setImmediate(this._ensureProcessing);}}_ensureProcessing = () => {//如果当前的激活的或者 说正在执行任务数行小于并发数while (this._activeTasks < this._parallelism) {const entry = this._queued.dequeue();//出队 先入先出if (entry === undefined) break;this._activeTasks++;//先让正在执行的任务数++entry.state = PROCESSING_STATE;//条目的状态设置为执行中this._startProcessing(entry);}this._willEnsureProcessing = false;}_startProcessing = (entry) => {this._processor(entry.item, (e, r) => {this._handleResult(entry, e, r);});}_handleResult = (entry, error, result) => {const callback = entry.callback;const callbacks = entry.callbacks;entry.state = DONE_STATE;//把条目的状态设置为已经完成entry.callback = undefined;//把callbackentry.callbacks = undefined;entry.result = result;//把结果赋给entryentry.error = error;//把错误对象赋给entrycallback(error, result);if (callbacks !== undefined) {for (const callback of callbacks) {callback(error, result);}}this._activeTasks--;if (this._willEnsureProcessing === false) {this._willEnsureProcessing = true;setImmediate(this._ensureProcessing);}}
}
module.exports = AsyncQueue;

参考

  • Node.js SDK
  • writing-a-plugin
  • api/plugins

钩子集合

  • wepback-plugin-visualizer

收集

        Object.keys(this.hooks).forEach(hookName => {const hook = this.hooks[hookName];if (hook instanceof HookMap) {for (let key of hook._map.keys()) {hook.for(key).tap('flow', () => {console.log(`|JavascriptParser|${hookName}|${hook.for(key).constructor.name}|${hook._args}|`);});}} else {hook.tap('flow', () => {console.log(`|JavascriptParser|${hookName}|${hook.constructor.name}|${hook._args}|`);});}});

触发时机

  • hooks
对象钩子名称类型参数含义
CompilerenvironmentSyncHook在编译器准备环境时调用,时机就在配置文件中初始化插件之后
CompilerafterEnvironmentSyncHook
CompilerentryOptionSyncBailHookcontext,entry
CompilerafterPluginsSyncHookcompiler
CompilerafterResolversSyncHookcompiler
CompilerinitializeSyncHook
CompilerbeforeRunAsyncSeriesHookcompiler
CompilerrunAsyncSeriesHookcompiler
CompilerinfrastructureLogSyncBailHookorigin,type,args
CompilerreadRecordsAsyncSeriesHook
CompilernormalModuleFactorySyncHooknormalModuleFactory
CompilercontextModuleFactorySyncHookcontextModuleFactory
CompilerbeforeCompileAsyncSeriesHookparams
CompilercompileSyncHookparams
CompilerthisCompilationSyncHookcompilation,params
CompilercompilationSyncHookcompilation,params
CompilermakeAsyncParallelHookcompilation
CompilationaddEntrySyncHookentry,options
NormalModuleFactorybeforeResolveAsyncSeriesBailHookresolveData
NormalModuleFactoryfactorizeAsyncSeriesBailHookresolveData
NormalModuleFactoryresolveAsyncSeriesBailHookresolveData
NormalModuleFactoryafterResolveAsyncSeriesBailHookresolveData
NormalModuleFactorycreateModuleAsyncSeriesBailHookcreateData,resolveData
NormalModuleFactorymoduleSyncWaterfallHookmodule,createData,resolveData
CompilationbuildModuleSyncHookmodule
CompilationnormalModuleLoaderSyncHookloaderContext,module
JavascriptParserprogramSyncBailHookast,comments
JavascriptParserpreStatementSyncBailHookstatement
JavascriptParserpreStatementSyncBailHookstatement
JavascriptParserblockPreStatementSyncBailHookdeclaration
JavascriptParserpreDeclaratorSyncBailHookdeclarator,statement
JavascriptParserblockPreStatementSyncBailHookdeclaration
JavascriptParserstatementSyncBailHookstatement
JavascriptParserdeclaratorSyncBailHookdeclarator,statement
JavascriptParserstatementSyncBailHookstatement
JavascriptParserfinishSyncBailHookast,comments
CompilationsucceedModuleSyncHookmodule
NormalModuleFactorybeforeResolveAsyncSeriesBailHookresolveData
NormalModuleFactoryfactorizeAsyncSeriesBailHookresolveData
NormalModuleFactoryresolveAsyncSeriesBailHookresolveData
NormalModuleFactoryafterResolveAsyncSeriesBailHookresolveData
NormalModuleFactorycreateModuleAsyncSeriesBailHookcreateData,resolveData
NormalModuleFactorymoduleSyncWaterfallHookmodule,createData,resolveData
CompilationbuildModuleSyncHookmodule
CompilationnormalModuleLoaderSyncHookloaderContext,module
JavascriptParserprogramSyncBailHookast,comments
JavascriptParserpreStatementSyncBailHookstatement
JavascriptParserblockPreStatementSyncBailHookdeclaration
JavascriptParserstatementSyncBailHookstatement
JavascriptParserfinishSyncBailHookast,comments
CompilationsucceedModuleSyncHookmodule
CompilationsucceedEntrySyncHookentry,options,module
CompilationlogSyncBailHookorigin,logEntry
CompilerfinishMakeAsyncSeriesHookcompilation
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationfinishModulesAsyncSeriesHookmodules
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationsealSyncHook
CompilationoptimizeDependenciesSyncBailHookmodules
CompilationlogSyncBailHookorigin,logEntry
CompilationafterOptimizeDependenciesSyncHookmodules
CompilationlogSyncBailHookorigin,logEntry
CompilationbeforeChunksSyncHook
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationafterChunksSyncHookchunks
CompilationlogSyncBailHookorigin,logEntry
CompilationoptimizeSyncHook
CompilationoptimizeModulesSyncBailHookmodules
CompilationafterOptimizeModulesSyncHookmodules
CompilationoptimizeChunksSyncBailHookchunks,chunkGroups
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationafterOptimizeChunksSyncHookchunks,chunkGroups
CompilationoptimizeTreeAsyncSeriesHookchunks,modules
CompilationafterOptimizeTreeSyncHookchunks,modules
CompilationoptimizeChunkModulesAsyncSeriesBailHookchunks,modules
CompilationafterOptimizeChunkModulesSyncHookchunks,modules
CompilationshouldRecordSyncBailHook
CompilationreviveModulesSyncHookmodules,records
CompilationbeforeModuleIdsSyncHookmodules
CompilationmoduleIdsSyncHookmodules
CompilationoptimizeModuleIdsSyncHookmodules
CompilationafterOptimizeModuleIdsSyncHookmodules
CompilationreviveChunksSyncHookchunks,records
CompilationbeforeChunkIdsSyncHookchunks
CompilationchunkIdsSyncHookchunks
CompilationoptimizeChunkIdsSyncHookchunks
CompilationafterOptimizeChunkIdsSyncHookchunks
CompilationlogSyncBailHookorigin,logEntry
CompilationrecordModulesSyncHookmodules,records
CompilationrecordChunksSyncHookchunks,records
CompilationoptimizeCodeGenerationSyncHookmodules
CompilationlogSyncBailHookorigin,logEntry
CompilationbeforeModuleHashSyncHook
CompilationlogSyncBailHookorigin,logEntry
CompilationafterModuleHashSyncHook
CompilationlogSyncBailHookorigin,logEntry
CompilationbeforeCodeGenerationSyncHook
CompilationlogSyncBailHookorigin,logEntry
CompilationafterCodeGenerationSyncHook
CompilationlogSyncBailHookorigin,logEntry
CompilationbeforeRuntimeRequirementsSyncHook
CompilationadditionalModuleRuntimeRequirementsSyncHookmodule,runtimeRequirements,context
CompilationadditionalModuleRuntimeRequirementsSyncHookmodule,runtimeRequirements,context
CompilationlogSyncBailHookorigin,logEntry
CompilationadditionalChunkRuntimeRequirementsSyncHookchunk,runtimeRequirements,context
CompilationlogSyncBailHookorigin,logEntry
CompilationadditionalTreeRuntimeRequirementsSyncHookchunk,runtimeRequirements,context
CompilationlogSyncBailHookorigin,logEntry
CompilationafterRuntimeRequirementsSyncHook
CompilationlogSyncBailHookorigin,logEntry
CompilationbeforeHashSyncHook
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationchunkHashSyncHookchunk,chunkHash,ChunkHashContext
CompilationcontentHashSyncHookchunk
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationfullHashSyncHookhash
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationafterHashSyncHook
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationrecordHashSyncHookrecords
CompilationlogSyncBailHookorigin,logEntry
CompilationbeforeModuleAssetsSyncHook
CompilationlogSyncBailHookorigin,logEntry
CompilationshouldGenerateChunkAssetsSyncBailHook
CompilationbeforeChunkAssetsSyncHook
CompilationrenderManifestSyncWaterfallHookresult,options
CompilationassetPathSyncWaterfallHookpath,options,assetInfo
CompilationchunkAssetSyncHookchunk,filename
CompilationlogSyncBailHookorigin,logEntry
CompilationadditionalChunkAssetsObjectundefined
CompilationadditionalAssetsObjectundefined
CompilationoptimizeAssetsAsyncSeriesHookassets
CompilationprocessAssetsAsyncSeriesHookassets
CompilationoptimizeChunkAssetsObjectundefined
CompilationafterOptimizeChunkAssetsObjectundefined
CompilationafterOptimizeAssetsSyncHookassets
CompilationafterProcessAssetsSyncHookassets
CompilationlogSyncBailHookorigin,logEntry
CompilationrecordSyncHookcompilation,records
CompilationneedAdditionalSealSyncBailHook
CompilationafterSealAsyncSeriesHook
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilerafterCompileAsyncSeriesHookcompilation
CompilationlogSyncBailHookorigin,logEntry
CompilershouldEmitSyncBailHookcompilation
CompileremitAsyncSeriesHookcompilation
CompilationassetPathSyncWaterfallHookpath,options,assetInfo
CompilerassetEmittedAsyncSeriesHookfile,info
CompilerafterEmitAsyncSeriesHookcompilation
CompilationlogSyncBailHookorigin,logEntry
CompilationneedAdditionalPassSyncBailHook
CompileremitRecordsAsyncSeriesHook
CompilationlogSyncBailHookorigin,logEntry
CompilerdoneAsyncSeriesHookstats
CompilationlogSyncBailHookorigin,logEntry
CompilationlogSyncBailHookorigin,logEntry
CompilershutdownAsyncSeriesHook
CompilationstatsNormalizeSyncHookoptions,context
CompilationstatsFactorySyncHookstatsFactory,options
CompilationstatsPrinterSyncHookstatsPrinter,options
CompilationprocessErrorsSyncWaterfallHookerrors
CompilationprocessWarningsSyncWaterfallHookwarnings
CompilerafterDoneSyncHookstats
CompilerinfrastructureLogSyncBailHookorigin,type,args

工作流

初始化阶段
事件名解释代码位置
读取命令行参数从命令行中读取用户输入的参数require(“./convert-argv”)(argv)
实例化 Compiler1.用上一步得到的参数初始化 Compiler 实例 2.Compiler 负责文件监听和启动编译 3.Compiler 实例中包含了完整的 Webpack 配置,全局只有一个 Compiler 实例。compiler = webpack(options);
加载插件1.依次调用插件的 apply 方法,让插件可以监听后续的所有事件节点。 同时给插件传入 compiler 实例的引用,以方便插件通过 compiler 调用 Webpack 提供的 API。plugin.apply(compiler)
处理入口读取配置的 Entrys,为每个 Entry 实例化一个对应的 EntryPlugin,为后面该 Entry 的递归解析工作做准备new EntryOptionPlugin().apply(compiler) new SingleEntryPlugin(context, item, name) compiler.hooks.make.tapAsync
编译阶段
事件名解释代码位置
run启动一次新的编译this.hooks.run.callAsync
compile该事件是为了告诉插件一次新的编译将要启动,同时会给插件传入compiler 对象。compile(callback)
compilation当 Webpack 以开发模式运行时,每当检测到文件变化,一次新的 Compilation 将被创建。 一个 Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。 Compilation 对象也提供了很多事件回调供插件做扩展。newCompilation(params)
make一个新的 Compilation 创建完毕主开始编译this.hooks.make.callAsync
addEntry即将从 Entry 开始读取文件compilation.addEntry this._addModuleChain
moduleFactory创建模块工厂const moduleFactory = this.dependencyFactories.get(Dep)
create创建模块moduleFactory.create
factory开始创建模块factory(result, (err, module) resolver(result this.hooks.resolver.tap(“NormalModuleFactory”
resolveRequestArray解析loader路径resolveRequestArray
resolve解析资源文件路径resolve
userRequest得到包括loader在内的资源文件的绝对路径用!拼起来的字符串userRequest
ruleSet.exec它可以根据模块路径名,匹配出模块所需的loaderthis.ruleSet.exec
_run它可以根据模块路径名,匹配出模块所需的loader_run
loaders得到所有的loader数组[results0].concat(loaders, results[1], results[2])
getParser获取AST解析器this.getParser(type, settings.parser)
buildModule开始编译模块this.buildModule(module buildModule(module, optional, origin, dependencies, thisCallback)
build开始真正编译入口模块build(options
doBuild开始真正编译入口模块doBuild
执行loader使用loader进行转换runLoaders runLoaders
iteratePitchingLoaders开始递归执行pitch loaderiteratePitchingLoaders
loadLoader加载loaderloadLoader
runSyncOrAsync执行pitchLoaderrunSyncOrAsync
processResource开始处理资源processResource options.readResource iterateNormalLoaders iterateNormalLoaders
createSource创建源代码对象this.createSource
parse使用parser转换抽象语法树this.parser.parse
parse解析抽象语法树parse(source, initialState)
acorn.parse解析语法树acorn.parse(code, parserOptions)
ImportDependency遍历并添加添加依赖parser.state.module.addDependency(clearDep)
succeedModule生成语法树后就表示一个模块编译完成this.hooks.succeedModule.call(module)
processModuleDependencies递归编译依赖的模块this.processModuleDependencies(module processModuleDependencies(module, callback) this.addModuleDependencies buildModule
make后结束makethis.hooks.make.callAsync(compilation, err => {}
finish编译完成compilation.finish();
结束阶段
事件名解释代码位置
seal封装compilation.seal seal(callback)
addChunk生成资源addChunk(name)
createChunkAssets创建资源this.createChunkAssets()
getRenderManifest获得要渲染的描述文件getRenderManifest(options)
render渲染源码source = fileManifest.render();
afterCompile编译结束this.hooks.afterCompile
shouldEmit所有需要输出的文件已经生成好,询问插件哪些文件需要输出,哪些不需要。this.hooks.shouldEmit
emit确定好要输出哪些文件后,执行文件输出,可以在这里获取和修改输出内容。this.emitAssets(compilation this.hooks.emit.callAsync const emitFiles = err this.outputFileSystem.writeFile
this.emitRecords写入记录this.emitRecords
done全部完成this.hooks.done.callAsync

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/26637.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(Git)多人协作1

文章目录 前言总结 前言 目标&#xff1a;master分支下file.txt文件新增“aaa”,“bbb” 实现&#xff1a;开发者1新增“aaa”,开发者2新增“bbb” 条件&#xff1a;在同一个分支下协作完成 实际开发过程中&#xff0c;每个用户都与属于自己的码云账户&#xff0c;如果想要进…

windows安装SQLyog

windows安装SQLyog 1. 下载 SQLyog 安装包 访问 SQLyog 的官方网站。在网站上找到下载链接&#xff0c;通常会有一个“Download”或“Try Now”按钮。如果需要注册或填写信息以获取下载链接&#xff0c;请按提示操作。 2. 运行安装程序 下载完成后&#xff0c;双击运行下载…

css设置滚动条样式;滚动条设置透明

滚动条透明代码 .resizable-div {resize: both;/* 允许水平和垂直调整大小 */overflow: auto;/* 确保内容超出边界时出现滚动条 */ } /* 滚动条整体样式 */ .resizable-div::-webkit-scrollbar {width: 4px; /* 竖直滚动条宽度 */height: 4px; /* 水平滚动条高度 */ }/* 滚动条…

湖州等保测评公司有几家?在那里?电话多少?

湖州等保测评公司有几家&#xff1f;在那里&#xff1f;电话多少&#xff1f; 【回答】&#xff1a;目前湖州暂未有正规等保测评机构。因此湖州企业可以就近选择浙江省内正规等保测评机构&#xff0c;有可以网上寻找合适的等保测评机构。 知识拓展1&#xff1a;浙江正规等保测…

Python学习笔记6:pychram相关知识及安装教程,后续需要学习的入门知识

上篇文章说了&#xff0c;今天去公司重新装一下IDE&#xff0c;最后也是把过程这边再记录一下&#xff0c;有需要的可以参考一下。 关于pychram pychram是什么&#xff1f; PyCharm是由JetBrains公司开发的一款流行的Python集成开发环境&#xff08;IDE&#xff09;。它专为…

Linux C/C++ socket

一、第一个网络通讯程序 网络通讯是指两台计算机中的程序进行传输数据的过程客户程序(端):指主动发起通讯的程序。服务程序(端/器):指被动的等待&#xff0c;然后为向它发起通讯的客户端提供服务。 /** 程序名&#xff1a;demo1.cpp&#xff0c;此程序用于演示socket的客户端…

洛谷P1305 新二叉树(树的基本遍历)

题目描述 输入一串二叉树&#xff0c;输出其前序遍历。 输入格式 第一行为二叉树的节点数 &#x1d45b;。(1≤&#x1d45b;≤26) 后面 &#x1d45b; 行&#xff0c;每一个字母为节点&#xff0c;后两个字母分别为其左右儿子。特别地&#xff0c;数据保证第一行读入的节点…

Apollo9.0 PNC源码学习之Control模块(一)—— 控制模块概览

0 前言 从planning的角度看control,首先需要了解的就是相关的数据接口,规划出的轨迹(路径+速度)发给Control模块去执行 modules/planning/planning_component/planning_component.cc planning模块发布轨迹信息 planning_writer_ = node_->CreateWriter<ADCTrajecto…

60行代码加速20倍: NEON实现深度学习OD任务后处理绘框

【前言】 本文版权属于GiantPandaCV&#xff0c;未经允许&#xff0c;请勿转载&#xff01; 最近在学neon汇编加速&#xff0c;由于此前OD任务发现在检测后处理部分使用OpenCV较为占用资源且耗时&#xff0c;遂尝试使用NEON做后处理绘框&#xff0c;以达到加速并降低CPU资源消耗…

Linux 中 “ 磁盘、进程和内存 ” 的管理

在linux虚拟机中也有磁盘、进程、内存的存在。第一步了解一下磁盘 一、磁盘管理 &#xff08;1.1&#xff09;磁盘了解 track&#xff08; 磁道 &#xff09; &#xff1a;就是磁盘上的同心圆&#xff0c;从外向里&#xff0c;依次排序1号&#xff0c;2号磁盘........等等。…

802.11中的各种帧

在无线网络中&#xff0c;802.11协议定义了三种类型的帧&#xff1a;管理帧&#xff08;Management Frames&#xff09;、控制帧&#xff08;Control Frames&#xff09;和数据帧&#xff08;Data Frames&#xff09;。每种类型的帧都有其特定的功能&#xff0c;帮助维护和管理…

QNX简述

文章目录 前言1. QNX简介1.1 什么是QNX1.2 QNX的应用场景1.3 QNX的优点1.4 QNX的发展史1.5 QNX的商业模式 2. QNX的技术特点3. QNX和其它操作系统的比较3.1 QNX VS LINUX3.2 QNX VS FreeRTOS3.3 QNX VS 鸿蒙操作系统 4. 我的疑问4.1 微内核看起来又稳定又容易调试&#xff0c;为…

【讯为Linux驱动开发】6.自旋锁spinlock

【自旋锁】 线程A获取自旋锁后&#xff0c;B假如想获取自旋锁则只能原地等待&#xff0c;仍占用CPU&#xff0c;不会休眠&#xff0c;直到获取自旋锁为止。 【函数】 DEFINE SINLOCK(spinlock t lock) 定义并初始化一个变量int spin lock init(spinlock t*lock) 初始化自…

技术速递|Java on Azure Tooling 5月更新 - Java 对 Azure 容器应用程序的入门指南支持

作者&#xff1a;Jialuo Gan 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎阅读 Java on Azure 工具 5 月份更新。在本次更新中&#xff0c;我们将介绍 Java 在 Azure 上的容器应用程序的入门指南。希望您喜欢这些更新&#xff0c;并享受使用 Azure 工具包的流畅体验。请下…

Python中用pip命令用稳定的国内源安装第三方库

近期发现python中安装三方库的最稳定的方式还是用pip命令&#xff0c;带上国内源的地址。 比如清华源&#xff1a; pip install 包名 -i https://pypi.tuna.tsinghua.edu.cn/simple/ 用这个带国内源的格式&#xff0c;非常稳定&#xff01;

audio标签怎么使用

<audio> 标签在 HTML 中用于嵌入音频内容&#xff0c;如音乐、歌曲、音效等。这个标签允许你在网页上直接播放音频&#xff0c;而无需依赖任何外部播放器或插件&#xff08;如过去的 Flash 插件&#xff09;。 以下是如何使用 <audio> 标签的基本示例&#xff1a;…

oss一个桶中如何创建多个文件夹并在上传文件时上传到相应指定的桶中

在阿里云OSS&#xff08;Object Storage Service&#xff09;中&#xff0c;文件夹的概念实际上是一个逻辑上的概念&#xff0c;因为OSS是一个基于对象的存储服务&#xff0c;而不是基于文件系统的。但是&#xff0c;你可以通过为对象指定特定的key来模拟文件夹结构。以下是如何…

《pvz植物大战僵尸杂交版》V2.0.88整合包火爆全网,支持安卓、ios、电脑等!

今天来给大家安利一款让人欲罢不能的游戏——《植物大战僵尸杂交版》2.0.88版。这可不是普通的植物大战僵尸&#xff0c;它可是席卷了B站&#xff0c;火爆全网的存在&#xff01; 先说说这个版本&#xff0c;它可是网络上现存最全的植物大战僵尸杂交版整合包。里面不仅有修改工…

什么是 OSI 模型?

OSI 模型&#xff08;开放式系统互联模型&#xff09;是一个由国际标准化组织&#xff08;ISO&#xff09;提出的概念模型&#xff0c;旨在为计算机网络的互联互通提供标准框架&#xff08;定义于 ISO/IEC 7498-1&#xff09;。该模型将通信系统中的数据流划分为七个层&#xf…

uni-app中添加路由拦截

uni-app中添加路由鉴权和路由拦截 在main.js中添加如下代码 let list ["navigateTo", "redirectTo", "reLaunch", "switchTab"] let routesWhitelist [/pages/tabs/classify,/pages/tabs/study,/pages/tabs/mine] // 可以直接跳转的…