代码语言:javascript复制
// 每日前端夜话 第438篇
// 正文共:3000 字
// 预计阅读时间:12 分钟
❝本文由作者 xfz 授权发布 ❞
插件的运行环境
- 插件没有像loader那样的独立的运行环境,run-loader
- 只能在webpack里面运行
插件基本结构
代码语言:javascript复制// 插件名称
class MyPlugin {
constructor(options) {}
// 插件运行方法apply
apply(compiler) {
// 插件hooks
compiler.hooks.done.tap('My Plugin', (/* xxx */) => {
// 插件处理逻辑
})
}
}
module.exports = MyPlugin
- 使用
plugins: [new MyPlugin()]
插件的错误处理
- 参数校验阶段可以直接throw出错误
- 通过compilation对象的warning和errors结构
compileration.warning.push('warning')
compileration.errors.push('error')
通过compilation进行文件写入
- Compilation上的 assets 可以用于文件写入
- 文件写入需要使用 webpack-sources
compilation.assets[name] = new RawSource('xxxx')
事件钩子会有不同的类型 SyncBailHook,AsyncSeriesHook,SyncHook等
如果是异步的事件钩子,那么可以使用 tapPromise 或者 tapAsync 来注册事件函数,
tapPromise 要求方法返回 Promise 以便处理异步,而 tapAsync 则是需要用 callback 来返回结果
代码语言:javascript复制compiler.hooks.done.tapPromise('PluginName', (stats) => {
return new Promise((resolve, reject) => {
// 处理promise的返回结果 reject(err) : resolve()
});
compiler.hooks.done.tapAsync('PluginName', (stats, callback) => {
callback( err))
});
除了同步和异步的,名称带有 parallel 的,注册的事件函数会并行调用,名称带有 bail 的,注册的事件函数会被顺序调用,直至一个处理方法有返回值名称带有 waterfall 的,
每个注册的事件函数,会将上一个方法的返回结果作为输入参数。有一些类型是可以结合到一起的,如 AsyncParallelBailHook,这样它就具备了更加多样化的特性
代码语言:javascript复制const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
// 在 compiler的hooks中注册一个方法,当执行到该阶段时会调用
compiler.hooks.run.tap(pluginName, compilation => {
console.log("The webpack build process is starting!!!");
});
}
}
使用
plugins: [ new ConsoleLogOnBuildWebpackPlugin() ]
自己动手写写插件
my-plugin
代码语言:javascript复制├── package.json
├── plugins
│ └── my-plugin.js
├── src
│ └── index.js
└── webpack.config.js
package.json
代码语言:javascript复制{
"name": "loader-order",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12"
}
}
webpack.config.js
代码语言:javascript复制const path = require('path');
const MyPlugin = require('./plugins/my-plugin')
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'main.js'
},
plugins: [
new MyPlugin('xfz')
]
}
my-plugin.js
代码语言:javascript复制class MyPlugin { // 插件名称
constructor(options) {
this.options = options
}
apply(compiler) {
console.log('my plugin is executed')
compiler.hooks.done.tap('My Plugin', (/* xxx */) => {
// 插件处理逻辑
console.log('my plugin options:', this.options)
})
}
}
module.exports = MyPlugin
index.js
代码语言:javascript复制const a = 1
npm run build,日志打印
代码语言:javascript复制my plugin is executed
my plugin options: xfz
zip-plugin
代码语言:javascript复制├── dist
│ ├── main.js
│ └── offline.zip
├── package.json
├── plugins
│ └── zip-plugin.js
├── src
│ └── index.js
└── webpack.config.js
package.json
代码语言:javascript复制{
"name": "zip-plugin",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"loader-utils": "^2.0.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"yazl": "^2.5.1"
},
"dependencies": {
"jszip": "^3.5.0"
}
}
webpack.config.js
代码语言:javascript复制const path = require('path');
const ZipPlugin = require('./plugins/zip-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'main.js'
},
plugins: [
new ZipPlugin({
filename: 'offline'
})
]
}
zip-plugin.js
代码语言:javascript复制// 将文件压缩为zip包
const JSZip = require('jszip')
const RawSource = require('webpack-sources').RawSource
const path = require('path')
const zip = new JSZip()
class ZipPlugin {
constructor(options) {
this.options = options
}
apply(compiler) {
// 在生成文件的钩子上操作
compiler.hooks.emit.tapAsync('ZipPlugin', (compilation, callback) => {
const folder = zip.folder(this.options.filename)
for (let filename in compilation.assets) {
const source = compilation.assets[filename].source()
folder.file(filename, source)
}
zip.generateAsync({ type: 'nodebuffer'})
.then(content => {
const outputPath = path.join(
compilation.options.output.path,
this.options.filename '.zip'
)
const outputRelativePath = path.relative(
compilation.options.output.path,
outputPath
)
compilation.assets[outputRelativePath] = new RawSource(content)
callback()
})
})
}
}
module.exports = ZipPlugin
index.js
代码语言:javascript复制const a = 1
npm run build,产出物
- main.js
- offline.zip
htmlAfterWebpackPlugin
- 前端缓存,将打包出来的 runtime、vendor、index文件存储在localStorage中,增量更新
- 初次加载时请求服务器,第二次加载则请求localStorage中存储的脚本