细说webpack源码之compile流程-rules参数处理技巧(1)

前端之家收集整理的这篇文章主要介绍了细说webpack源码之compile流程-rules参数处理技巧(1)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

上篇文章给大家介绍了

大家可以点击查看。

Tips:写到这里,需要对当初的规则进行修改。在必要的地方,会在webpack.config.js中设置特殊的参数来跑源码,例如本例会使用module:{rules:[...]}来测试,基本上测试参数均取自于vue脚手架(太复杂的删掉)。

  下面两节的主要流程图如下:

  在进入compile方法后,迎面而来的就是这么一行代码

  简单看一下方法,源码如下:

{ const params = { normalModuleFactory: this.createNormalModuleFactory(),contextModuleFactory: this.createContextModuleFactory(),compilationDependencies: [] }; return params; } // 工厂方法1 Compiler.prototype.createNormalModuleFactory = () => { const normalModuleFactory = new NormalModuleFactory(this.options.context,this.resolvers,this.options.module || {}); this.applyPlugins("normal-module-factory",normalModuleFactory); return normalModuleFactory; } // 工厂方法2 Compiler.prototype.createContextModuleFactory = () => { const contextModuleFactory = new ContextModuleFactory(this.resolvers,this.inputFileSystem); this.applyPlugins("context-module-factory",contextModuleFactory); return contextModuleFactory; }

  该方法生成了一个对象参数,而对象中的值又分别调用了各自的工厂方法

  按顺序,首先来看NormalModuleFactory工厂方法,首先是构造函数

{...} // options => options.module constructor(context,resolvers,options) { super(); // 该参数在WebpackOptionsApply方法中被赋值 this.resolvers = resolvers; // 处理module.rules或module.loaders // 两者意义一样 this.ruleSet = new RuleSet(options.rules || options.loaders); // 谁会去设置这玩意???默认参数设置那会被置为true // 这里会生成一个返回布尔值的函数 可以简单理解为!! this.cachePredicate = typeof options.unsafeCache === "function" ? options.unsafeCache : Boolean.bind(null,options.unsafeCache); this.context = context || ""; // 解析缓存 this.parserCache = {}; // 这两个方法超级长 this.plugin("factory",() => (result,callback) => { /**/ }); this.plugin("resolver",() => (data,callback) => { /**/ }) } }

  也是一个继承了Tapable框架的类,构造函数除去两个事件流注入不看,剩余的内容只有RuleSet那一块,也是本节的主要内容

  从参数可以看出,这里是对module.rules处理的地方,本节中测试配置添加了rules方便测试:

path.join(__dirname,'..',path) module.exports = { entry: './input.js',output: { filename: 'output.js' },module: { rules: [ { test: /\.js$/,loader: 'babel-loader',include: [resolve('src'),resolve('test')] },{ test: /\.css/,loader: 'css-loader!style-loader' } ] } };

  只针对配置中有或者常用且官网有解释的参数进行。

RuleSet

  构造函数如下:

  生成了一个纯净对象并调用了本地静态方法进行格式化:

传进来的配置数组 // refs => 纯净对象 // ident => 'ref-' static normalizeRules(rules,refs,ident) { // 分数组与非数组 if (Array.isArray(rules)) { return rules.map((rule,idx) => { return RuleSet.normalizeRule(rule,`${ident}-${idx}`); }); } else if (rules) { return [RuleSet.normalizeRule(rules,ident)]; } // 未配置rules直接返回空数组 else { return []; } } }

  这里也区分了数组参数与非数组参数,但是有个小bug。

  看代码可以很容易猜到,数组与非数组的情况理论上是这样的:

  因为传非数组,会被包装成一个数组,所以这种情况属于单loader配置。

  但是,这样配置是会报错的,因为过不了validateSchema的验证,测试结果如图:

  这就很尴尬了,提供了非数组形式的处理方式,但是又不通过非数组的校验,所以这基本上是永远不会被执行的代码

  管他的,估计源码太大,开发者已经顾不上了。

  下面正式进行格式化阶段,源码整理如下:

'ref-${index}' static normalizeRule(rule,ident) { // 传入字符串 // 'css-loader' => use:[{loader:'css-loader'}] if (typeof rule === "string") return { use: [{ loader: rule }] }; // 非法参数 if (!rule) throw new Error("Unexcepted null when object was expected as rule"); if (typeof rule !== "object") throw new Error("Unexcepted " + typeof rule + " when object was expected as rule (" + rule + ")"); const newRule = {}; let useSource; let resourceSource; let condition; // test if (rule.test || rule.include || rule.exclude) { /**/ } if (rule.resource) { /**/ } // 官网doc都懒得解释 估计是几个很弱智的参数 if (rule.resourceQuery) { /**/ } if (rule.compiler) { /**/ } if (rule.issuer) { /**/ } // loader、loaders只能用一个 if (rule.loader && rule.loaders) throw new Error(RuleSet.buildErrorMessage(rule,new Error("Provided loader and loaders for rule (use only one of them)"))); const loader = rule.loaders || rule.loader; // 处理loader if (typeof loader === "string" && !rule.options && !rule.query) { checkUseSource("loader"); newRule.use = RuleSet.normalizeUse(loader.split("!"),ident); } else if (typeof loader === "string" && (rule.options || rule.query)) { checkUseSource("loader + options/query"); newRule.use = RuleSet.normalizeUse({ loader: loader,options: rule.options,query: rule.query },ident); } else if (loader && (rule.options || rule.query)) { throw new Error(RuleSet.buildErrorMessage(rule,new Error("options/query cannot be used with loaders (use options for each array item)"))); } else if (loader) { checkUseSource("loaders"); newRule.use = RuleSet.normalizeUse(loader,ident); } else if (rule.options || rule.query) { throw new Error(RuleSet.buildErrorMessage(rule,new Error("options/query provided without loader (use loader + options)"))); } // 处理use if (rule.use) { checkUseSource("use"); newRule.use = RuleSet.normalizeUse(rule.use,ident); } // 递归处理内部rules if (rule.rules) newRule.rules = RuleSet.normalizeRules(rule.rules,`${ident}-rules`); // 不知道是啥 if (rule.oneOf) newRule.oneOf = RuleSet.normalizeRules(rule.oneOf,`${ident}-oneOf`); // const keys = Object.keys(rule).filter((key) => { return ["resource","resourceQuery","compiler","test","include","exclude","issuer","loader","options","query","loaders","use","rules","oneOf"].indexOf(key) < 0; }); keys.forEach((key) => { newRule[key] = rule[key]; }); function checkUseSource(newSource) { /**/ } function checkResourceSource(newSource) { /**/ } if (Array.isArray(newRule.use)) { newRule.use.forEach((item) => { if (item.ident) { refs[item.ident] = item.options; } }); } return newRule; } }

  总体来看源码内容如下:

1、生成newRules对象保存转换后的rules

2、处理单字符串rule

3、处理test、include、exclude参数

4、处理resource、resourseQuery、compiler、issuer参数

5、处理loader、loaders、options、query参数

6、处理use参数

7、递归处理rules参数

8、处理oneOf参数

总结

以上所述是小编给大家介绍的细说webpack源码之compile流程-rules参数处理技巧(1)。编程之家 jb51.cc 收集整理的教程希望能对你有所帮助,如果觉得编程之家不错,可分享给好友!感谢支持

原文链接:https://www.f2er.com/js/34491.html

猜你在找的JavaScript相关文章