Plugins
插件,是一个带有apply方法的对象。可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
插件的目的是为了增强webpack在项目自动化构建方面的能力,范围更广,用途更多
插件的组成
- 一个JavaScript命名函数
- 在插件函数的prototype上定义一个apply方法
- 指定一个绑定到webpack自身的事件钩子
- 处理webpack内部实例的特定数据
- 功能完成后调用webpack提供的回调
常见的应用场景:
清除打包之前的目录
自动生成html文件
压缩文件
自动发布打包结果到服务器实现自动部署
原理
插件使用钩子机制,webpack在每一个环节提供一个钩子,在不同的环节挂载不同的任务
通过loader处理特殊资源的加载,通过plugin实现自动化的构建任务,例如:自动压缩、自动发布
plugins:[
new HtmlWebpackPlugin({ // 打包html资源
template:'./src/index.html', // 复制 ./src/index.html文件,并自动引入打包输出的所有资源
}),
]
插件的生命周期钩子
钩子 | 说明 | 参数 | 类型 |
---|---|---|---|
afterPlugins | 启动一次新的编译 | compiler | 同步 |
compile | 创建compilation对象之前 | compilationParams | 同步 |
compilation | compilation对象创建完成 | compilation | 同步 |
emit | 资源生成完成,输出之前 | compilation | 异步 |
afterEmit | 资源输出到目录完成 | compilation | 异步 |
done | 完成编译 | stats | 同步 |
entryOption | 在处理了webpack选项的entry配置后调用 | context,entry | SyncbailHook |
自定义插件
tapable
Tapable暴露了tap、tapAsync和tapPromise方法,根据钩子的同步/异步方式选择一个方法注入逻辑
tap:同步钩子。不能包含异步调用
tapAsync:异步钩子,通过callback回调告诉webpack异步执行完毕
tapPromise:异步钩子,返回一个promise告诉webpack异步执行完毕
compiler和compilation的区别
compiler对象表示不变的webpack环境,是针对webpack的
compilation对象针对的是随时可变的项目文件,只要文件有改动,compilation就会被重新创建
实现
// 校验插件配置的工具库
const {
validate
} = require('schema-utils');
// node内置模块
const path = require('path');
const util = require('util');
const webpack = require('webpack');
const {
RawSource
} = webpack.sources
const globby = require('globby');
const {
schema
} = require('webpack-dev-server');
const {
readFile
} = require('fs');
class CopyWebpackPlugin {
constructor(options = {}) {
// 验证options是否符合规范
validate(schema, options, {
name: 'CopyWebpackPlugin'
});
this.options = options;
}
apply(compiler) {
// 初始化complation
compiler.hooks.thisCompilation('CopyWebpackPlugin', (compilation) => {
compilation.hooks.additionalAssets.tapAsync('CopyWebpackPlugin', async (cb) => {
// 将from中的资源复制到to中,输出出去
const {
from,
ignore
} = this.options;
const to = this.options.to ? this.options.to : '.';
// 1. 读取from中的所有资源
// context就是webpack配置
// 运行指令的目录
const context = compilation.options.context;
// 将输入路径变成绝对路径
const absolutePath = path.isAbsolute(from) ? from : path.resolve(from);
// 1. 过滤掉ignore文件
const paths = await globby(absolutePath, {
ignore
});
// 2. 读取from中的所有资源
const files = Promise.all(path.map(async (absolutePath) => {
const data = await readFile(absolutePath);
const relativePath = path.basename(absolutePath);
const fileName = path.join(to, relativePath);
return {
data,
fileName
}
}))
// 3. 生成webpack格式的文件
const assets = files.map((file) => {
const source = new RawSource(file.data);
return {
source,
filename: file.filename
}
});
// 4. 添加compliation中,输出出去
assets.forEach(asset => {
compilation.emitAsset(asset.filename, asset.source);
});
cb();
});
});
}
}
module.exports = CopyWebpackPlugin;