webpack源码分析之三:loader
前言
在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
(完)
相关推荐
不知道该写啥QAQ 2020-11-12
webfullStack 2020-11-09
Yvettre 2020-09-15
想做大牛的蜗牛 2020-10-30
gloria0 2020-10-26
gaojie0 2020-09-11
SelinaChan 2020-08-14
不知道该写啥QAQ 2020-08-09
gloria0 2020-08-09
不知道该写啥QAQ 2020-08-02
hline 2020-07-29
SelinaChan 2020-07-28
wangdianyong 2020-07-23
webpackvuees 2020-07-23
yqoxygen 2020-07-20
不知道该写啥QAQ 2020-07-18
waterv 2020-07-18
81463166 2020-07-17