Hello Webpack Plugin
Webpack Internal Plugin Relation
WebPack 插件開(kāi)發(fā)實(shí)例
Webpack API
Webpack Modules
File System
child_process
從Webpack源碼探究打包流程
Introduction
Webpack 插件開(kāi)發(fā)可以參考官方提供的 html-webpack-plugin
集绰、 clean-webpack-plugin
或 webpack-manifest-plugin
這些插件作為參考,推薦使用 ES6 語(yǔ)法寫(xiě)插件,Webpack 2.0 就開(kāi)始支持 ES6聂示。
其次要了解 Webpack 內(nèi)部插件與鉤子關(guān)系就乓,參考[Webpack Internal Plugin Relation],了解 Compolation
、Compiler
腹忽、Parser
建芙、MainTemplate
基本對(duì)象結(jié)構(gòu)没隘。
Webpack 在執(zhí)行過(guò)程不同的階段執(zhí)行插件方法,開(kāi)始時(shí)執(zhí)行 apply(compiler)
方法會(huì)將當(dāng)前的編譯器實(shí)例傳入禁荸,編譯器包含了一組鉤子函數(shù) compiler.hooks
供插件使用右蒲。compiler.options
包含了 Webpack 的配置文件內(nèi)容,這些配置拼合了默認(rèn)的選項(xiàng)赶熟。
實(shí)列化 Webpack 時(shí)瑰妄,沒(méi)有設(shè)置回調(diào)函數(shù),Webpack 就會(huì)返回一個(gè) Compiler 實(shí)例:
const webpack = require('webpack');
const compiler = webpack({
// Configuration Object
});
compiler.run((err, stats) => { // Stats Object
// ...
});
const watching = compiler.watch({
// Example watchOptions
aggregateTimeout: 300,
poll: undefined
}, (err, stats) => { // Stats Object
// Print watch/build result here...
console.log(stats);
});
基于 Node.js, Compiler 實(shí)例提供了基本的文件系統(tǒng) inputFileSystem映砖,outputFileSystem间坐,可以這樣來(lái)定制:
const MemoryFS = require('memory-fs');
const webpack = require('webpack');
const fs = new MemoryFS();
const compiler = webpack({ / options / });
compiler.outputFileSystem = fs;
compiler.run((err, stats) => {
// Read the output later:
const content = fs.readFileSync('...');
});
在使用 webpack-dev-server
開(kāi)發(fā)時(shí),就是使用 CachedInputFileSystem
和 MemoryFileSystem
邑退。Webpack 內(nèi)部還實(shí)現(xiàn)了幾個(gè)文件系統(tǒng) NodeWatchFileSystem
竹宋、 NodeOutputFileSystem
、 NodeJsInputFileSystem
用于不同的場(chǎng)景地技。對(duì)磁盤(pán)文件的讀寫(xiě)還是通過(guò) Node.js 提供的 [File System] 文件系統(tǒng)模塊實(shí)現(xiàn)的逝撬。
完成后要執(zhí)行的動(dòng)作通過(guò) compiler.hooks.done
鉤子來(lái)注冊(cè),Webpack 會(huì)將 Compilation 實(shí)例傳入乓土。compilation.hooks
也提供了一些鉤子函數(shù)宪潮。注冊(cè)鉤子的方法參考以下代碼溯警,不同鉤子回調(diào)函數(shù)的參數(shù)會(huì)有所差別:
compiler.hooks.someHook.tap('MyPlugin', (params) => {
});
compilation.hooks.buildModule.tap('MyPlugin',
module => {
module.useSourceMap = true;
}
);
Webpack 的 Module 是模塊化編程的基本單元結(jié)構(gòu),支持的模塊類型有 CoffeeScript狡相、 TypeScript梯轻、 ESNext (Babel)、 Sass尽棕、 Less喳挑、 Stylus 等等,Webpack 系統(tǒng)的 Loader 也是模塊的一種滔悉。在 buildModule
鉤子中可以讀取 module.type
伊诵、module.constructor
確定模塊類型。
compiler.hooks.done
鉤子在編譯完成時(shí)執(zhí)行回官,回調(diào)參數(shù)是一個(gè)統(tǒng)計(jì)信息對(duì)象曹宴,compilation 中有包含了 compiler 和編譯模塊 modules 信息:
{
compilation: ...
hash: '00354804c2f21f00c528',
startTime: 1563516897225,
endTime: 1563516905223
}
關(guān)鍵鉤子作用 鉤子類型 回調(diào)參數(shù) 解析
beforeRun
AsyncSeriesHook Compiler 運(yùn)行前的準(zhǔn)備活動(dòng),主要啟用了文件讀取的功能歉提。
run
AsyncSeriesHook Compiler 跑起來(lái)了笛坦,在編譯之前有緩存,則啟用緩存苔巨,這樣可以提高效率版扩。
beforeCompile
AsyncSeriesHook params 開(kāi)始編譯前的準(zhǔn)備,創(chuàng)建的ModuleFactory侄泽,創(chuàng)建Compilation礁芦,并綁定ModuleFactory到Compilation上。
compile
SyncHook params 編譯了
make
AsyncParallelHook compilation 從Compilation的addEntry函數(shù)悼尾,開(kāi)始構(gòu)建模塊
afterCompile
AsyncSeriesHook compilation 編譯結(jié)束了
shouldEmit
SyncBailHook compilation 獲取compilation發(fā)來(lái)的電報(bào)柿扣,確定編譯時(shí)候成功,是否可以開(kāi)始輸出了诀豁。
emit
AsyncSeriesHook compilation 輸出文件了
afterEmit
AsyncSeriesHook compilation 輸出完畢
done
AsyncSeriesHook Stats 無(wú)論成功與否窄刘,一切已塵埃落定窥妇。
Coding Now
現(xiàn)在就來(lái)寫(xiě)一個(gè)插件舷胜,用來(lái)將項(xiàng)目根目錄下的 index.html
模板拷貝到發(fā)布目錄下。利用 Shell 來(lái)拷貝 public 目錄的資源活翩。在 Node.js 中使用 child_process 執(zhí)行 shell 有三種方法烹骨,spawn,exec和execFile材泄。假定 Windows 系統(tǒng)沮焕,借用了 xcopy 命令來(lái)復(fù)制目錄結(jié)構(gòu)。同時(shí)使用了 iconv-lite
做默認(rèn)的 Windows 系統(tǒng) GB2312 編碼的默認(rèn)的 Node.js 編碼 UTF-8 的轉(zhuǎn)換拉宗。
const fs = require('fs');
const exec = require('child_process').execSync;
var iconv = require('iconv-lite');
class AssetsCopier{
constructor(options){
this.options = options || {};
console.log('Plugin options', this.options);
}
apply(compiler){
let path = compiler.options.output.path;
let buf = exec(`xcopy ${process.cwd()}\\public ${path} /Y /S /I`, err => {
if(err) console.log(err);
});
let str = iconv.decode(buf, "gb2312");
console.log("? apply", str);
// fs.copyFile(src, dest[, flags], callback)
fs.readFile(process.cwd()+'/index.html', 'utf8', function (err, data) {
if (err) return console.log("HTML Template not found "+err.path);
fs.writeFile(path+'/index.html', data, err => {
console.log( err? "Fail to copy index.html":"Copy index.html to "+path)
});
});
if (compiler.hooks) {
compiler.hooks.done.tap('AssetsCopier', this.onDone);
// compiler.hooks.compilation.tap('AssetsCopier', this.onCompilation);
compiler.hooks.afterResolvers.tap('AssetsCopier', this.onAfterResolve);
// } else { for old webpack
// compiler.plugin('done', this.onDone);
// compiler.plugin('compilation', this.onCompilation);
// compiler.plugin('normal-module-factory', (nmf) => {
// nmf.plugin('after-resolve', this.onAfterResolve);
// });
}
}
onAfterResolve(compiler){
console.log("? onAfterResolve");
}
onCompilation(compilation, compilationParams){
console.log("? onCompilation");
compilation.hooks.buildModule.tap('AssetsCopier',
module => {
console.log(`\n? buildModule ${module.type}\n`, module.constructor.toString().split("{")[0]);
}
);
}
onDone(stats){
console.log("? onDone");
}
}
module.exports = AssetsCopier;
現(xiàn)在峦树,把以上代碼保存到 assets-copier.js
并在 Webpack 配置文件中引入插件辣辫,然后就可以源打包,Webpack 就會(huì)執(zhí)行插件了魁巩。
const AssetsCopier = require("./assets-copier");
module.exports = {
// ...
plugins: [
new AssetsCopier (),
]
}
API
lib/Compiler.js
創(chuàng)建的hook(鉤子)
Compiler.hooks.additionalPass
Compiler.hooks.afterCompile
Compiler.hooks.afterEmit
Compiler.hooks.afterEnvironment
Compiler.hooks.afterPlugins
Compiler.hooks.afterResolvers
Compiler.hooks.beforeCompile
Compiler.hooks.beforeRun
Compiler.hooks.compilation
Compiler.hooks.compile
Compiler.hooks.contextModuleFactory
Compiler.hooks.done
Compiler.hooks.emit
Compiler.hooks.entryOption
Compiler.hooks.environment
Compiler.hooks.failed
Compiler.hooks.invalid
Compiler.hooks.make
Compiler.hooks.normalModuleFactory
Compiler.hooks.run
Compiler.hooks.shouldEmit
Compiler.hooks.thisCompilation
Compiler.hooks.watchClose
Compiler.hooks.watchRun
無(wú)注冊(cè)的hook(鉤子)
調(diào)用的hook(鉤子)
Compiler.call.additionalPass - callAsync
Compiler.call.afterCompile - callAsync
Compiler.call.afterEmit - callAsync
Compiler.call.beforeCompile - callAsync
Compiler.call.beforeRun - callAsync
Compiler.call.childCompiler - call
Compiler.call.compilation - call
Compiler.call.compile - call
Compiler.call.contextModuleFactory - call
Compiler.call.done - callAsync
Compiler.call.emit - callAsync
Compiler.call.make - callAsync
Compiler.call.needAdditionalPass - call
Compiler.call.normalModuleFactory - call
Compiler.call.run - callAsync
Compiler.call.shouldEmit - call
Compiler.call.thisCompilation - call
lib/Compilation.js
創(chuàng)建的hook(鉤子)
Compilatio.hooks.additionalAssets
Compilatio.hooks.additionalChunkAssets
Compilatio.hooks.advancedOptimizeModuleOrder
Compilatio.hooks.afterChunks
Compilatio.hooks.afterHash
Compilatio.hooks.afterOptimizeAssets
Compilatio.hooks.afterOptimizeChunkAssets
Compilatio.hooks.afterOptimizeChunkIds
Compilatio.hooks.afterOptimizeChunkModules
Compilatio.hooks.afterOptimizeChunks
Compilatio.hooks.afterOptimizeDependencies
Compilatio.hooks.afterOptimizeExtractedChunks
Compilatio.hooks.afterOptimizeModuleIds
Compilatio.hooks.afterOptimizeModules
Compilatio.hooks.afterOptimizeTree
Compilatio.hooks.afterSeal
Compilatio.hooks.assetPath
Compilatio.hooks.beforeChunkAssets
Compilatio.hooks.beforeChunkIds
Compilatio.hooks.beforeChunks
Compilatio.hooks.beforeHash
Compilatio.hooks.beforeModuleAssets
Compilatio.hooks.beforeModuleIds
Compilatio.hooks.buildModule
Compilatio.hooks.childCompiler
Compilatio.hooks.chunkAsset
Compilatio.hooks.chunkHash
Compilatio.hooks.contentHash
Compilatio.hooks.dependencyReference
Compilatio.hooks.failedModule
Compilatio.hooks.finishModules
Compilatio.hooks.finishRebuildingModule
Compilatio.hooks.moduleAsset
Compilatio.hooks.moduleIds
Compilatio.hooks.needAdditionalPass
Compilatio.hooks.needAdditionalSeal
Compilatio.hooks.normalModuleLoader
Compilatio.hooks.optimize
Compilatio.hooks.optimizeAssets
Compilatio.hooks.optimizeChunkAssets
Compilatio.hooks.optimizeChunkIds
Compilatio.hooks.optimizeChunkModules
Compilatio.hooks.optimizeChunkModulesAdvanced
Compilatio.hooks.optimizeChunkModulesBasic
Compilatio.hooks.optimizeChunkOrder
Compilatio.hooks.optimizeChunks
Compilatio.hooks.optimizeChunksAdvanced
Compilatio.hooks.optimizeChunksBasic
Compilatio.hooks.optimizeDependencies
Compilatio.hooks.optimizeDependenciesAdvanced
Compilatio.hooks.optimizeDependenciesBasic
Compilatio.hooks.optimizeExtractedChunks
Compilatio.hooks.optimizeExtractedChunksAdvanced
Compilatio.hooks.optimizeExtractedChunksBasic
Compilatio.hooks.optimizeModuleIds
Compilatio.hooks.optimizeModuleOrder
Compilatio.hooks.optimizeModules
Compilatio.hooks.optimizeModulesAdvanced
Compilatio.hooks.optimizeModulesBasic
Compilatio.hooks.optimizeTree
Compilatio.hooks.rebuildModule
Compilatio.hooks.record
Compilatio.hooks.recordChunks
Compilatio.hooks.recordHash
Compilatio.hooks.recordModules
Compilatio.hooks.reviveChunks
Compilatio.hooks.reviveModules
Compilatio.hooks.seal
Compilatio.hooks.shouldGenerateChunkAssets
Compilatio.hooks.shouldRecord
Compilatio.hooks.succeedModule
Compilatio.hooks.unseal
調(diào)用的hook(鉤子)