前言
在webpack特性里面,它可以支持将非javaScript文件打包,但前面写到webpack的模块化打包只能应用于含有特定规范的JavaScript文件。本次介绍的loader则是用来解决这类问题的。本文章loader的实现基于code-splitting
功能分析
举个例子:
webpack.config.js中的配置loader
module: {
rules: [
{
test: /\.js$/,
loader: "test-loader!test-loader2"
}
]
}
业务代码中的内联loader
require('d!c');
分析:
我们需要将这些loader解析成可运行的函数,并在parse模块解析语法树之前运行掉这些loader函数
所以我们需要:
- resolve模块:分析出module对应的loader字符串,并解析出各个loader的绝对路径
- buildDeps模块:通过文件路径获取需要运行的loader函数,将其压入队列,之后再依次按序递归出loader函数运行,如果是异步loader,则要通过回调函数来递归下一个loader函数。
实现
resolve 模块
实现思路:
- 将配置内的loaders,shell命令的loaders,require/import的内联loader从前至后组成一个数组。配置内的loaders需要正则匹配test属性,来获取配置内的loader字符串。所有loader字符串内部又可以截取'!',获取完整的loader。
- 分析并解析该数组内的字符串,获取各个loader的绝对路径。并返回解析好的字符串。这块的实现和文件打包类似。
最终require内的字符串如下
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/d.js!
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/test-loader/index.js!
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/test-loader2/index.js!
/Users/zhujian/Documents/workspace/webpack/simple-webpack/example/node_modules/c.js
buildDeps模块
实现思路:
- 解析文件路径,并获取需要运行的loader函数,存入数组
- 数组在通过pop函数一个个递归,考虑到存在异步loader函数的情况,需要为运行函数提供async,以及callback的上下文。具体的上下文可参考Loader API
loader递归逻辑如下:
nextLoader.apply(null, content);
function nextLoader() {
const args = Array.prototype.slice.apply(arguments);
if (loaderFunctions.length > 0) {
const loaderFunction = loaderFunctions.pop();
let async = false;
const context = {
fileName,
options,
debug: options.debug,
async: function () {
async = true;
return nextLoader;
},
callback: function () {
async = true;
nextLoader.apply(null, arguments)
}
};
const resVal = loaderFunction.apply(context, args);
if (!async) {
nextLoader(resVal);
}
} else {
callback(null, args[0])
}
}
测试
将以上3个loader,test-loader,test-loader2,异步loader d.js,打包c.js
test-loader
module.exports = function(content) {
return content+"\nexports.answer = 42;\n"
}
test-loader2
module.exports = function(content) {
return content+"\nexports.test2 = test2;\n"
}
异步loader d.js
module.exports = function (content) {
const d = 'd';
this.async();
const b = content + "\nexports.d = 2000;\n";
setTimeout(this.callback.bind(this, b), 2000);
}
c.js
const c = 'c';
module.exports = c;
最终打包出来的c.js的代码如下
....
/* 1 */
/***/(function(module, exports,__webpack_require__) {
const c = 'c';
module.exports = c;
exports.test2 = test2;
exports.answer = 42;
exports.d = 2000;
/***/}
....
代码实现
本人的简易版webpack实现simple-webpack
(完)