原稿:语雀 · 实现一个 loader · 原目录:Webpack › Demo › 实现一个 loader
虽然了解了 loader 的概念,但动手实现一个哪怕再简单不过的 loader 也比单纯的纸面了解更加深印象
目标 & 初始化
目标:我们打算实现一个 txt 文件的 loader,准确说是两个:
- reverse-loader:将 txt 文件的字符逆序
- toUpperClass-loader:将逆序后的字符全部转为大写
最后希望输出一个 js 文件,这个 bundle 会被 index.html 通过 <script> 加载,在控制台打印处理后的字符
初始化如下,无需其他的 loader 和 plugin
# 目录创建
mkdir txt_loader
cd txt_loader
# npm 初始化
npm init -y
# 安装 webpack 相关包
cnpm install webpack webpack-cli --save-dev
创建 src/loaders,其中创建两个需要的 js 文件:reverse-loader.js,toUpperClass-loader.js
创建 dist/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>loader</title>
</head>
<body>
<script src="app.js"></script>
</body>
</html>
单一原则: 每个 Loader 只做一件事,因此我们将两个简单任务分给两个 loader,第一个 loader 将处理后的返回值会链式传递给第二个loader ... ... 链中的最后一个 loader 应该返回 webpack 所期望的 JavaScript 代码 |
loader 引入
创建 webpack.config.js
const path = require("path");
module.exports = {
mode: "production",
entry: {
app: "./src/test.txt",
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.txt$/,
use: [
path.resolve(__dirname, "src/loaders/toUpperCase-loader.js"),
path.resolve(__dirname, "./src/loaders/reverse-loader.js"),
],
},
],
}
};
- 最重要的就是关于两个 loader 的引入,不同于 node_modules 中模块的引入,自定义的 loader 需要给出路径,也可以通过 resolveLoader 设置,这里直接使用路径
- 其次注意调用关系,use 中先是 toUpperCase-loader,后是 reverse-loader,但实际的调用链一定是从后向前的
loader 编写
loader 运行在 Node.js 中,通常预期将 loader 模块导出为一个函数,并且编写为 Node.js 兼容的 JS 代码。loader 本事作为函数,其执行自然分同步异步,以下也分成同步和异步编写
同步
// reverse-loader.js
function loader(source){
source = source.split('').reverse().join('');
return source;
}
module.exports = loader;
toUpperCase-loader 作为链上最后一个 loader,应当以字符形式返回 JS 代码给 webpack
// toUpperCase-loader.js
function loader(source) {
source = source.toUpperCase();
return `console.log('${source}')`;
}
module.exports = loader;
npm run build 同步构建结果

异步
reverse-loader.js 不变,修改 toUpperCase-loader.js,通过 setTimeout 模拟异步
// toUpperCase-loader.jsfunction loader(source) { const callback = this.async(); new Promise((resolve) => { setTimeout(() => { resolve(source.toUpperCase()); }, 1000); }).then((source) => callback(null, `console.log('${source}')`));}module.exports = loader;
异步通过 this.async API 实现,它返回一个 callback 函数,调用时可以接收四个参数,第一个必须是 Error 或 null;第二个是一个 string 或 Buffer 对象,用于返回异步执行的结果
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
异步 loader 构建的结果:app.js 中生成了 js 代码,构建时间 1224 ms 确实说明经历了异步执行过程

完整代码见:https://github.com/NepJNQ/Front-end-Baby/tree/master/webpack/txt-loader