自定义实现webpack插件原理解析
Posted 哈娄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义实现webpack插件原理解析相关的知识,希望对你有一定的参考价值。
webpack插件构成部分
- 一个具名javascript函数
- 在他的原型上定义apply方法
- 指定一个触及到 webpack本身的事件钩子
- 操作webpack内部的特定实例数据
- 实现功能后调用 webpack提供的callback函数
webpack基本架构
插件有一个构造函数实例化出来, 构造函数定义apply方法,在安装插件时,apply方法会被webpack的compiler调用一次。apply方法可以接受一个webpack的compiler对象的引用, 从而可以在回调函数中访问到compiler对象。
插件钩子和触发方法简介
Webpack 提供钩子有很多,这里简单介绍几个
- entryOption:在 webpack 选项中的
entry
配置项 处理过之后,执行插件。 afterPlugins
: 设置完初始插件之后,执行插件。compilation
: 编译创建之后,生成文件之前,执行插件。emit
: 生成资源到output
目录之前。done
: 编译完成。
Webpack 提供三种触发钩子的方法:
tap
:以同步方式触发钩子(支持两个参数,插件名称compilation 和回调函数callback)tapAsync
:以异步方式触发钩子;tapPromise
:以异步方式触发钩子,返回 Promise;\\
官网提供简单的插件结构
class HelloWorldPlugin {
apply(compiler) {
compiler.hooks.done.tap('Hello World Plugin', (
stats /* 在 hook 被触及时,会将 stats 作为参数传入。 */
) => {
console.log('Hello World!');
});
}
}
module.exports = HelloWorldPlugin;
使用插件
// webpack.config.js
var HelloWorldPlugin = require('hello-world');
module.exports = {
// ... 这里是其他配置 ...
plugins: [new HelloWorldPlugin({ options: true })]
};
自定义实现插件需求:动态添加script标签
我们先在模版中做个标记,后面进行标记替换
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack 插件开发入门</title>
</head>
<body>
<!-- other code -->
<!--SetScriptTimestampPlugin inset script-->
</body>
</html>
我们插件应该是要在 HTML 输出之前,动态添加 script
标签,所以我们选择钩子是 compilation
阶段
compilation 继承于compiler
,包含 compiler
所有内容(也有 Webpack 的 options
),而且也有 plugin
函数接入任务点。
class SetScriptTimestampPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('SetScriptTimestampPlugin',
(compilation, callback) => {
// 插件逻辑 调用compilation提供的plugin方法
compilation.plugin(
"html-webpack-plugin-before-html-processing",
function(htmlPluginData, callback) {
// 读取并修改 script 上 src 列表
let jsScr = htmlPluginData.assets.js[0];
htmlPluginData.assets.js = [];
let result = `
<script>
let scriptDOM = document.createElement("script");
let jsScr = "./${jsScr}";
scriptDOM.src = jsScr + "?" + new Date().getTime();
document.body.appendChild(scriptDOM)
</script>
`;
let resultHTML = htmlPluginData.html.replace(
"<!--SetScriptTimestampPlugin inset script-->", result
);
// 返回修改后的结果
htmlPluginData.html = resultHTML;
}
);
}
);
}
}
module.exports = SetScriptTimestampPlugin;
在上面插件逻辑中,具体做了这些事
1. 执行 compilation.plugin
方法,并传入两个参数:插件事件和回调方法
- 所谓“插件事件”即插件所提供的一些事件,用于监听插件状态
2.获取脚本文件名称列表并清空
- 在回调方法中,通过
htmlPluginData.assets.js
获取需要通过script
引入的脚本文件名称列表,拷贝一份,并清空原有列表
3.编写替换逻辑
- 动态创建一个
script
标签,将其src
值设置为上一步读取到的脚本文件名,并在后面拼接 时间戳 作为参数。
4.插入替换逻辑
- 通过
htmlPluginData.html
可以获取到模版文件的字符串输出,我们只需要将模版字符串中替换入口<!--SetScriptTimestampPlugin inset script-->
替换成我们上一步编写的替换逻辑即可。
5.返回HTML文件
- 最后将修改后的 HTML 字符串,赋值给原来的
htmlPluginData.html
达到修改效果
最后使用插件
// webpack.config.js
const SetScriptTimestampPlugin = require("./SetScriptTimestampPlugin.js");
module.exports = {
// ... 省略其他配置
plugins: [
// ... 省略其他插件
new SetScriptTimestampPlugin()
]
}
以上是关于自定义实现webpack插件原理解析的主要内容,如果未能解决你的问题,请参考以下文章