资源读取插件实现
自定义 webpack 插件前,先了解一下如何实现同步插件和异步插件。
1. 基本环境准备
创建一个名为 webpack_plugins 的文件夹。
mkdir webpack-plugins
进入此文件夹,创建 package.json 文件。
cd webpack-plugins
npm init
安装 webpack 及 webpack-cli 工具。
npm i webpack webpack-cli --save-dev
创建 src 文件夹及 src/index.js 文件,编写测试脚本。
mkdir src && cd src && touch index.js
/**
* @file 入口文件
* @module src/index.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
console.log('hello world.');
根目录下创建 webpack.config.js 文件,并编写配置文件。
touch webpack.config.js
/**
* @file webapck配置文件
* @module webpack.config.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
/**
* @requires node_modules/path node内置模块
*/
const path = require('path');
// 导出webpack配置
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
运行 npx webpack 命令,出现 dist 目录 及 bundle.js 文件,说明配置完成。
2. 同步插件实现
(1)创建plugins文件夹
根目录下创建 plugins 文件夹,用于存放自定义的 webpack 插件。
mkdir plugins
(2)创建DonePlugin.js文件
在 plugins 文件夹中创建 DonePlugin.js 文件,用于编写同步插件。
cd plugins && touch DonePlugin.js
(3)编写同步插件
webpack 插件其实就是一个类,类中需要实现 apply 方法,webpack 运行时会调用该方法并为该方法注入 compiler 对象。
Compiler 对象包含了当前运行Webpack的配置,包括entry、output、loaders等配置,这个对象在启动Webpack时被实例化,而且是全局唯一的。Plugin可以通过该对象获取到Webpack的配置信息进行处理。
/**
* @file 同步插件
* @module plugins/DonePlugin.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
/**
* @class DonePlugin
* @classdesc 同步插件
*/
class DonePlugin {
/**
* @description webpack插件运行使用的方法
* @param {object} compiler - webpack运行时自动注入
* @return {string}
*/
apply (compiler) {
compiler.hooks.done.tap('DonePlugin', (status) => {
console.log('同步插件编译完成');
});
}
}
// 导出同步插件
module.exports = DonePlugin;
在上面的代码中,可以看到,我们可以使用 compiler 对象中的 tap 方法注册插件名称。
(4)使用同步插件
webpack.config.js 文件中引入并使用同步插件。
/**
* @file webapck配置文件
* @module webpack.config.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
/**
* @requires node_modules/path node内置模块
* @requires plugins/DonePlugin 同步插件
*/
const path = require('path'),
DonePlugin = require('./plugins/DonePlugin');
// 导出webpack配置
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new DonePlugin()
]
}
好了,下面运行 npx webpack 命令打包测试。
如上图所示,出现 “同步插件编译完成” 字样,说明同步插件编写完成。
3. 异步插件实现
(1)创建AsyncPlugin.js文件
在 plugins 文件夹中创建 AsyncPlugin.js 文件,用于编写异步插件。
cd plugins && touch AsyncPlugin.js
(2)编写异步插件
异步插件有两种实现方式,一种是回调函数的方式,一种是 promise 的方式。
回调函数的方式。
/**
* @file 异步插件
* @module plugins/AsyncPlugin.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
/**
* @class AsyncPlugin
* @classdesc 异步插件
*/
class AsyncPlugin {
/**
* @description webpack插件运行使用的方法
* @param {object} compiler - webpack运行时自动注入
* @return {string}
*/
apply (compiler) {
compiler.hooks.emit.tapAsync('AsyncPlugin', (compilation, cb) => {
setTimeout(() => {
console.log('emit done.');
cb();
}, 1000)
});
}
}
// 导出异步插件
module.exports = AsyncPlugin;
promise 的方式。
/**
* @file 异步插件
* @module plugins/AsyncPlugin.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
/**
* @class AsyncPlugin
* @classdesc 异步插件
*/
class AsyncPlugin {
/**
* @description webpack插件运行使用的方法
* @param {object} compiler - webpack运行时自动注入
* @return {string}
*/
apply (compiler) {
compiler.hooks.emit.tapPromise('AsyncPlugin', (compilation) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('emit done.');
resolve();
}, 1000);
});
});
}
}
// 导出异步插件
module.exports = AsyncPlugin;
两种方式都可以实现功能,个人推荐使用 promise 的方式。
(3)使用异步插件
webpack.config.js 文件中引入并使用异步插件。
/**
* @file webapck配置文件
* @module webpack.config.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
/**
* @requires node_modules/path node内置模块
* @requires plugins/DonePlugin 同步插件
* @requires plugins/AsyncPlugin 异步插件
*/
const path = require('path'),
DonePlugin = require('./plugins/DonePlugin'),
AsyncPlugin = require('./plugins/AsyncPlugin');
// 导出webpack配置
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new DonePlugin(),
new AsyncPlugin()
]
}
编写完成后,使用 npx webpack 命令进行测试。
出现 “emit done.” 字样,说明异步插件编写完成。
5. 资源读取插件实现
插件主要的功能是记录打包后 dist 目录中文件的大小和名称。
(1)编写测试文件
src 目录下创建 index.html 和 index.css 文件。
cd src && touch index.html && index.css
在 index.html 和 index.css 中编写测试代码。
/* 入口文件样式 */
body {
background-color: orange;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>yuelou</title>
</head>
<body>
</body>
</html>
入口文件中引入 index.css 文件。
/**
* @file 入口文件
* @module src/index.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
/**
* @requires src/index.css 入口文件的样式
*/
require('./index.css');
console.log('hello world.');
plugins 文件中创建 AssetPlugin.js 文件。
cd plugins && touch AssetPlugin.js
(2) 编写webpack配置文件
安装 html-webpack-plugin 插件,用于生成 html 文件。
npm i html-webpack-plugin --save-dev
安装 css-loader 和 mini-css-extract-plugin,用于处理并生成 css 文件。
npm i css-loader mini-css-extract-plugin --save-dev
引入并使用 webpack 插件和自定义的 AssetPlugin 插件。
/**
* @file webapck配置文件
* @module webpack.config.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
/**
* @requires node_modules/path node内置模块
* @requires plugins/DonePlugin 同步插件
* @requires plugins/AsyncPlugin 异步插件
* @requires plugins/AssetPlugin 资源读取插件
* @requires node_modules/HtmlWebpackPlugin html文件生成插件
* @requires node_modules/MiniCssExtractPlugin CSS文件生成插件
*/
const path = require('path'),
DonePlugin = require('./plugins/DonePlugin'),
AsyncPlugin = require('./plugins/AsyncPlugin'),
AssetPlugin = require('./plugins/AssetPlugin'),
HtmlWebpackPlugin = require('html-webpack-plugin'),
MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 导出webpack配置
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin(),
new DonePlugin(),
new AsyncPlugin(),
new AssetPlugin({
filename: 'assets-list.md'
})
]
}
自定义的 AssetPlugin 插件可以配置生成的文件名称。
(3)编写AssetPlugin插件
在 webpack 的 emit 阶段,我们可以使用同步钩子 tap 函数注册插件名称,可以在 compilation 对象中获取到需要处理的文件资源。
Compilation对象可以理解编译对象,包含了模块、依赖、文件等信息。在开发模式下运行Webpack时,每修改一次文件都会产生一个新的Compilation对象,Plugin可以访问到本次编译过程中的模块、依赖、文件内容等信息。
/**
* @file 资源读取插件
* @module plugins/AssetPlugin.js
* @version 0.1.0
* @author yueluo <yueluo.yang@qq.com>
* @time 2020-07-04
*/
/**
* @class AssetPlugin
* @classdesc 资源读取插件
*/
class AssetPlugin {
/**
* @constructor 构造函数
* @param {string} filename - 文件名称
* @return {void}
*/
constructor ({ filename }) {
this.filename = filename;
}
/**
* @description webpack插件运行使用的方法
* @param {object} compiler - webpack运行时自动注入
* @return {string}
*/
apply (compiler) {
compiler.hooks.emit.tap('AssetPlugin', (compilation) => {
const assets = compilation.assets;
let content = `## 文件名 大小\r\n`;
// 创建文件内容
Object.entries(assets).forEach(([filename, stat]) => {
content += `- ${filename} ${stat.size()}\r\n`;
});
assets[this.filename] = {
source () {
return content;
},
size () {
return content.length;
}
}
});
}
}
// 导出资源读取插件
module.exports = AssetPlugin;
插件编写完成后,运行 npx webpack 命令进行打包测试。
如上图所示,如果 dist 目录下生成 assets-list.md 文件,并且正常记录打包后的资源文件信息,说明资源读取插件编写完成。
6. 总结
本篇文章介绍了如何实现自己的 webpack 插件,希望能对大家有所帮助。