亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

請問該如何寫一個webpack插件?

請問該如何寫一個webpack插件?

江戶川亂折騰 2019-09-23 20:16:52
如何寫一個webpack插件
查看完整描述

4 回答

?
慕村9548890

TA貢獻1884條經驗 獲得超4個贊

其實有兩種方案:第一種是將 css 文件在 js entry 中添加依賴;第二種直接設置 css entry。
第一種方案
// index.jsimport 'normalize.css';
...
// webpack config{ entry: { index: './index.js'
},
...
}
// outputindex.jsindex.css

這種是 Webpack 官方推薦的方案,但是每次都要把 css 放到 js entry 中才可以 extract 出來。
第二種方案(直接設置 css entry)
默認 Webpack 設置 css entry 除了 extract 出 css 文件還會多產生一個 js 文件,其實可以寫個 Webpack 插件將其刪除就可以了。


查看完整回答
反對 回復 2019-09-25
?
湖上湖

TA貢獻2003條經驗 獲得超2個贊


how to write a plugin這個教程還是可以好好看看的,尤其是那個simple example,它會教你在compilation的emit事件或之前,將你需要生成的文件放到webpack的compilation.assets里,這樣就可以借助webpack的力量幫你生成文件,而不需要自己手動去寫fs.writeFileSync。
主要就是這段代碼
compilation.assets['filelist.md'] = {
source: function() {
return filelist;
},
size: function() {
return filelist.length;
}
};

基本特性介紹
首先,定義一個函數func,用戶設置的options基本就在這里處理。
其次,需要設一個func.prototype.apply函數。這個函數是提供給webpack運行時調用的。webpack會在這里注入compiler對象。
輸出complier對象,你會看到這一長串的內容,初步一看,我看出了兩大類(有補充的可以告訴我)。一個webpack運行時的參數,例如_plugins,這些數組里的函數應該是webpack內置的函數,用于在compiltion,this-compilation和should-emit事件觸發時調用的。另一個是用戶寫在webpack.config.js里的參數。隱約覺得這里好多未來都可能會是webpack暴露給用戶的接口,使webpack的定制化功能更強大。
Compiler {
_plugins:
{ compilation: [ [Function], [Function], [Function], [Function] ],
'this-compilation': [ [Function: bound ] ],
'should-emit': [ [Function] ] },
outputPath: '',
outputFileSystem: null,
inputFileSystem: null,
recordsInputPath: null,
recordsOutputPath: null,
records: {},
fileTimestamps: {},
contextTimestamps: {},
resolvers:
{ normal: Tapable { _plugins: {}, fileSystem: null },
loader: Tapable { _plugins: {}, fileSystem: null },
context: Tapable { _plugins: {}, fileSystem: null } },
parser:
Parser {
_plugins:
{ 'evaluate Literal': [Object],
'evaluate LogicalExpression': [Object],
'evaluate BinaryExpression': [Object],
'evaluate UnaryExpression': [Object],
'evaluate typeof undefined': [Object],
'evaluate Identifier': [Object],
'evaluate MemberExpression': [Object],
'evaluate CallExpression': [Object],
'evaluate CallExpression .replace': [Object],
'evaluate CallExpression .substr': [Object],
'evaluate CallExpression .substring': [Object],
'evaluate CallExpression .split': [Object],
'evaluate ConditionalExpression': [Object],
'evaluate ArrayExpression': [Object],
'expression Spinner': [Object],
'expression ScreenMod': [Object] },
options: undefined },
options:
{ entry:
{
'index': '/Users/mac/web/src/page/index/main.js' },
output:
{ publicPath: '/homework/features/model/',
path: '/Users/mac/web/dist',
filename: 'js/[name].js',
libraryTarget: 'var',
sourceMapFilename: '[file].map[query]',
hotUpdateChunkFilename: '[id].[hash].hot-update.js',
hotUpdateMainFilename: '[hash].hot-update.json',
crossOriginLoading: false,
hashFunction: 'md5',
hashDigest: 'hex',
hashDigestLength: 20,
sourcePrefix: '\t',
devtoolLineToLine: false },
externals: { react: 'React' },
module:
{ loaders: [Object],
unknownContextRequest: '.',
unknownContextRecursive: true,
unknownContextRegExp: /^\.\/.*$/,
unknownContextCritical: true,
exprContextRequest: '.',
exprContextRegExp: /^\.\/.*$/,
exprContextRecursive: true,
exprContextCritical: true,
wrappedContextRegExp: /.*/,
wrappedContextRecursive: true,
wrappedContextCritical: false },
resolve:
{ extensions: [Object],
alias: [Object],
fastUnsafe: [],
packageAlias: 'browser',
modulesDirectories: [Object],
packageMains: [Object] },
plugins:
[ [Object],
[Object],
[Object],
[Object],
NoErrorsPlugin {},
[Object],
[Object] ],
devServer: { port: 8081, contentBase: './dist' },
context: '/Users/mac/web/',
watch: true,
debug: false,
devtool: false,
cache: true,
target: 'web',
node:
{ console: false,
process: true,
global: true,
setImmediate: true,
__filename: 'mock',
__dirname: 'mock' },
resolveLoader:
{ fastUnsafe: [],
alias: {},
modulesDirectories: [Object],
packageMains: [Object],
extensions: [Object],
moduleTemplates: [Object] },
optimize: { occurenceOrderPreferEntry: true } },
context: '/Users/mac/web/' }

除此以外,compiler還有一些如run, watch-run的方法以及compilation, normal-module-factory對象。我目前用到的,主要是compilation。其它的等下一篇有機會再說。
對比起compiler還有compiler.plugin函數。這個相當于是插件可以進行處理的webpack的運行中的一些任務點,webpack就是完成一個又一個任務而完成整個打包構建過程的。如make是最開始的起點, complie就是編譯任務點,after-complie是編譯完成,emit是即將準備生成文件,after-emit是生成文件之后等等,前面幾個都是比較生動形象的任務點。
至于compilation,它繼承于compiler,所以能拿到一切compiler的內容(所以你也會看到webpack的options),而且也有plugin函數來接入任務點。在compiler.plugin('emit')任務點輸出compilation,會得到大致下面的對象數據,因為實在太長,我只保留了最重要的assets部份:
assetsCompilation {
assets:
{ 'js/index/main.js':
CachedSource {
_source: [Object],
_cachedSource: undefined,
_cachedSize: undefined,
_cachedMaps: {} } },
errors: [],
warnings: [],
children: [],
dependencyFactories:
ArrayMap {
keys:
[ [Object],
[Function: MultiEntryDependency],
[Function: SingleEntryDependency],
[Function: LoaderDependency],
[Object],
[Function: ContextElementDependency],
values:
[ NullFactory {},
[Object],
NullFactory {} ] },
dependencyTemplates:
ArrayMap {
keys:
[ [Object],
[Object],
[Object] ],
values:
[ ConstDependencyTemplate {},
RequireIncludeDependencyTemplate {},
NullDependencyTemplate {},
RequireEnsureDependencyTemplate {},
ModuleDependencyTemplateAsRequireId {},
AMDRequireDependencyTemplate {},
ModuleDependencyTemplateAsRequireId {},
AMDRequireArrayDependencyTemplate {},
ContextDependencyTemplateAsRequireCall {},
AMDRequireDependencyTemplate {},
LocalModuleDependencyTemplate {},
ModuleDependencyTemplateAsId {},
ContextDependencyTemplateAsRequireCall {},
ModuleDependencyTemplateAsId {},
ContextDependencyTemplateAsId {},
RequireResolveHeaderDependencyTemplate {},
RequireHeaderDependencyTemplate {} ] },
fileTimestamps: {},
contextTimestamps: {},
name: undefined,
_currentPluginApply: undefined,
fullHash: 'f4030c2aeb811dd6c345ea11a92f4f57',
hash: 'f4030c2aeb811dd6c345',
fileDependencies: [ '/Users/mac/web/src/js/index/main.js' ],
contextDependencies: [],
missingDependencies: [] }

assets部份重要是因為如果你想借助webpack幫你生成文件,你需要像官方教程how to write a plugin在assets上寫上對應的文件信息。
除此以外,compilation.getStats()這個函數也相當重要,能得到生產文件以及chunkhash的一些信息,如下:
assets{ errors: [],
warnings: [],
version: '1.12.9',
hash: '5a5c71cb2accb8970bc3',
publicPath: 'xxxxxxxxxx',
assetsByChunkName: { 'index/main': 'js/index/index-4c0c16.js' },
assets:
[ { name: 'js/index/index-4c0c16.js',
size: 453,
chunks: [Object],
chunkNames: [Object],
emitted: undefined } ],
chunks:
[ { id: 0,
rendered: true,
initial: true,
entry: true,
extraAsync: false,
size: 221,
names: [Object],
files: [Object],
hash: '4c0c16e8af4d497b90ad',
parents: [],
origins: [Object] } ],
modules:
[ { id: 0,
identifier: 'multi index/main',
name: 'multi index/main',
index: 0,
index2: 1,
size: 28,
cacheable: true,
built: true,
optional: false,
prefetched: false,
chunks: [Object],
assets: [],
issuer: null,
profile: undefined,
failed: false,
errors: 0,
warnings: 0,
reasons: [] },
{ id: 1,
identifier: '/Users/mac/web/node_modules/babel-loader/index.js?presets[]=es2015?sets[]=react!/Users/mac/web/src/js/main/index.js',
name: './src/js/index/main.js',
index: 1,
index2: 0,
size: 193,
cacheable: true,
built: true,
optional: false,
prefetched: false,
chunks: [Object],
assets: [],
issuer: 'multi index/main',
profile: undefined,
failed: false,
errors: 0,
warnings: 0,
reasons: [Object],
source: '' // 具體文件內容}
],
filteredModules: 0,
children: [] }

這里的chunks數組里,是對應會生成的文件,以及md5之后的文件名和路徑,里面還有文件對應的chunkhash(每個文件不同,但如果你使用ExtractTextPlugin將css文件獨立出來的話,它會與require它的js入口文件共享相同的chunkhash),而assets.hash則是統一的hash,對每個文件都一樣。值得關注的是chunks里的每個文件,都有source這一項目,提供給開發者直接拿到源文件內容(主要是js,如果是css且使用ExtractTextPlugin,則請自行打印出來參考)。
例子
接下來,會以最近我寫的一個插件 html-res-webpack-plugin 作為引子,來介紹基本的寫插件原理。插件的邏輯就寫在index.js里。
首先,將用戶輸入的參數在定好的函數中處理,HtmlResWebpackPlugin。
然后,新增apply函數,在里面寫好插件需要切入的webpack任務點。目前HtmlResWebpackPlugin插件只用到emit這個任務點,其它幾個僅作為演示。
第三步,調用addFileToWebpackAsset方法,寫compilation.assets,借助webpack生成html文件。
第四步,在開發模式下(isWatch = true),直接生成html,但在生產模式下(isWatch = true),插件會開始對靜態資源(js,css)進行md5或者內聯。
第五步,調用findAssets方法是為了通過compilation.getStats()拿到的數據,去匹配對應的靜態資源,還有找到對應的哈希(是chunkhash還是hash)。
最六步,調用addAssets方法,對靜態資源分別做內聯或者md5文件處理。內聯資源的函數是inlineRes,你會看到我使用了compilation.assets[hashFile].source() 及 compilation.assets[hashFile].children[1]._value。前者是針對于js的,后者是針對使用了ExtractTextPlugin的css資源。
最后一步,即是內聯和md5完成后,再更新一下compilation.assets中對應生成html的source內容,才能正確地生成內聯和md5后的內容。
后記
有興趣可以試用一下 html-res-webpack-plugin 這個插件(為什么要寫一個新的html生成插件,我在readme里寫了,此處不贅述),看看有哪些用得不爽之處。目前只是第一版,還不適合用于生產環境。希望第二版的時候能適用于更多的場景,以及性能更好。到是,我也會寫第二篇插件開發文章,將本文還沒提到的地方一一補充完整。也歡迎大家在這里發貼,或者指出本人的謬誤之處。



查看完整回答
反對 回復 2019-09-25
  • 4 回答
  • 0 關注
  • 601 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號