webpack原理 (4)

若是想讲handlerLoader1的loader中替换的内容通过配置自定义处理呢?就像是url-loader那样传递一个配置选项options,然后在loader中进行接受并处理。可以通过loader-utils的getOptions提取loader中的options进行处理,老版本是通过thus.query来进行处理

修改loader文件handlerLoader1

const loaderUtils = require('loader-utils') // 将js文件中的 信息 换成 通过options传递的name module.exports = function (source) { const optionsName = loaderUtils.getOptions(this).name || '' return source.replace(/信息/g, optionsName) }

修改webpack的loader

{ test:/\.js/, use:{ loader: './loader/loader1.js', options:{ name:'新的信息' } } }

使用npx webpack打包之后,便可以通过options配置进行替换

若是handlerLoader1处理完的东西还需要交给下一个loader进行处理之后,这样就会牵扯到多个同级loader的情况,将handlerLoader1拷贝两份,分别命名为handlerLoader11和handlerLoader12,内容可保持原有的,只是在原有的函数中分别打印其对应的loader的文件名称,因为只是为了看看loader的加载。

handlerLoader1的内容为

// 将js文件中的 信息 换成 msg module.exports = function (source) { console.log('我是 handlerLoader1'); // 其余两loader 的log分别为 handlerLoader2 handlerLoader3 return source.replace(/信息/g, 'msg') }

webpack配置loader

{ test:/\.js/, use:[ './loader/handlerLoader1.js', './loader/handlerLoader2.js', './loader/handlerLoader3.js' ] }

执行webpack打包,输出结果,可以得出loader的默认顺序是由右到左

>>> 我是 handlerLoader3 >>> 我是 handlerLoader2 >>> 我是 handlerLoader1

若修改webpack的loader为

{ test:/\.js/, use:['./loader/loader1.js'] },{ test:/\.js/, use:['./loader/loader2.js'] },{ test:/\.js/, use:['./loader/loader3.js'] },

执行webpack打包,输出结果,可以得出loader的默认顺序是由下到上的

>>> 我是 handlerLoader3 >>> 我是 handlerLoader2 >>> 我是 handlerLoader1 添加loader功能

通过自制一个loader之后,可以总结得到webpack支持loader的功能,主要就是4步

读取配置文件webpack.config.js的module.rulesloader配置项,进行倒序迭代

根据正则匹配到对应的文件类型,同时再批量导入loader函数

倒序迭代调用所有loader函数

返回处理后的代码

在webpack-theory中增加处理loader的能力,无非就是在加载每个模块的时候,根据配置的rules的正则进行匹配需要的资源,满足条件之后就会加载并使用对应的loader进行处理并迭代调用

需要注意的是,在什么时候去执行loader呢,在每次获取模块依赖的时候,都需要进行loader的test匹配,若是匹配到就加载对应的loader进行处理。例如本文的案例代码存在三个js文件,首先会加载index.js,在加载解析index的依赖之前就需要对其进行倒序便利全部的loader,若是匹配到对应的loader就会加载对应的loader对index.js的内容进行处理,因为index引入了parent.js,接下来便会在递归调用depAnalyse方法解析parnet之前进行判断和处理,child.js同理。

在depAnalyse方法中每次解析以来之前添加如下代码:

// 内部定义一个处理loader的函数 const _handleLoader = (usePath, _this) => { const loaderPath = path.join(this.root, usePath) const loader = require(loaderPath) source = loader.call(_this, source) } // 读取 rules 规则, 进行倒序遍历 const rules = this.rules for (let i = rules.length - 1; i >= 0; i--) { const { test, use } = rules[i] // 匹配 modulePath 是否符合规则,若是符合规则就需要倒序遍历获取所有的loader // 获取每一条规则,和当前的 modulePath 进行匹配 if (test.test(modulePath)) { // use 可能是 数组、对象、字符串 console.log(use); if (Array.isArray(use)) { // array for (let j = use.length - 1; j >= 0; j--) { // const loaderPath = path.join(this.root, use[j]) // const loader = require(loaderPath) // source = loader(source) _handleLoader(use[j]) } } else if (typeof use === 'string') { // string _handleLoader(use) } else if (use instanceof Object) { // object _handleLoader(use.loader, { query: use.options }) } } }

loader基础的相关编写到此为止,但是还是需要多加练习的思考,这里仅仅是演示了最简单的,大家可以参考官方文档进行loader的enforce、异步loader等知识点的深入学习和查看babel、sass-loader等社区优秀loader进行深入的理解和练习。

plugin

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdwzs.html