热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

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

webpack作为一种流行的打包工具被广泛应用在web项目的前端工程化构建中。下面通过本文给大家介绍webpack源码之compile流程-rules参数处理技巧,感兴趣的朋友一起看看吧

上篇文章给大家介绍了细说webpack源码之compile流程-rules参数处理技巧(2), 细说webpack源码之compile流程-入口函数run

大家可以点击查看。

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

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

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

const params = this.newCompilationParams();

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

Compiler.prototype.newCompilatiOnParams= () => {
 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工厂方法,首先是构造函数:

class NormalModuleFactory extends Tapable {
 // context默认为命令执行路径
 // resolvers => {...}
 // 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方便测试:

const path = require('path');
// resolve => 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

  构造函数如下:

module.exports = class RuleSet {
 constructor(rules) {
 // 空对象
 this.references = Object.create(null);
 // 格式化
 this.rules = RuleSet.normalizeRules(rules, this.references, "ref-");
 }
}

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

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

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

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

// 数组
module.exports = {
 // ...
 module: {
 rules: [{ test: /\.vue$/, loader: 'vue-loader' }, /*...*/ ]
 }
};
// 非数组
module.exports = {
 // ...
 module: {
 rules: { test: /\.vue$/, loader: 'vue-loader' }
 }
};

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

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

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

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

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

class RuleSet {
 constructor(rules) { /**/ };
 // ident => 'ref-${index}'
 static normalizeRule(rule, refs, 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, refs, `${ident}-rules`);
 // 不知道是啥
 if (rule.oneOf)
 newRule.OneOf= RuleSet.normalizeRules(rule.oneOf, refs, `${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),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


推荐阅读
  • 本文详细介绍了使用JavaScript和jQuery进行页面加载初始化的方法,包括不同的实现方式及其应用场景,并探讨了两者在初始化过程中的主要区别。 ... [详细]
  • 本文介绍了如何通过自定义配置类,利用 `WebMvcConfigurer` 接口来扩展 Spring MVC 的功能,实现默认首页的自动跳转,同时避免使用 `@EnableWebMvc` 注解全面接管 Spring MVC 的默认配置。 ... [详细]
  • 深入理解希尔排序算法
    本文详细介绍了希尔排序的原理及其相对于传统插入排序的优势,并通过实例解析了希尔排序的具体实现过程,包括代码示例及性能分析。 ... [详细]
  • 免费获取:全面更新的Linux集群视频教程及配套资源
    本资源包含最新的Linux集群视频教程、详细的教学资料、实用的学习课件、完整的源代码及多种软件开发工具。百度网盘链接:https://pan.baidu.com/s/1roYoSM0jHqa3PrCfaaaqUQ,提取码:41py。关注我们的公众号,获取更多更新的技术教程。 ... [详细]
  • 在J2EE开发领域,众多专业术语如PO、VO、BO、DTO、POJO及DAO常常令初学者感到困惑。本文旨在清晰解释这些术语及其相互间的关系,帮助开发者更好地理解和运用这些概念。 ... [详细]
  • 本文提供了关于WSDL(Web Services Description Language)的详细参考资料链接,包括官方文档和深入解析,旨在帮助开发者更好地理解和使用WSDL进行Web服务的开发与集成。 ... [详细]
  • 前端常用的布局类型——前端布局
    1.Static静态布局固定宽高:2.Liquid流式布局宽高用百分比,按屏幕分辨率调整,布局不发生变化3.Adaptive自适应 ... [详细]
  • 优化使用Apache + Memcached-Session-Manager + Tomcat集群方案
    本文探讨了使用Apache、Memcached-Session-Manager和Tomcat集群构建高性能Web应用过程中遇到的问题及解决方案。通过重新设计物理架构,解决了单虚拟机环境无法真实模拟分布式环境的问题,并详细记录了性能测试结果。 ... [详细]
  • 本文详细介绍了 HTML DOM 中的 document.getElementsByTagName 方法,通过实例说明其用法和应用场景。 ... [详细]
  • 在使用Firefox浏览器打开本地HTML文件时,尝试调用Canvas的drawImage方法可能会遇到NS_ERROR_NOT_AVAILABLE错误。本文探讨了这一问题的原因及解决方案。 ... [详细]
  • 本毕业设计探讨了通过图像处理技术对道路表面进行区域增强及整体检测的方法,旨在提高道路维护效率和安全性。 ... [详细]
  • C语言编程课程第十二课
    本课程将深入探讨C语言中的数组操作与基本算法实现,包括最大最小值交换、数组旋转以及约瑟夫环问题等经典案例分析。 ... [详细]
  • Python库在GIS与三维可视化中的应用
    Python库极大地扩展了GIS的能力,使其能够执行复杂的数据科学任务。本文探讨了几个关键的Python库,这些库不仅增强了GIS的核心功能,还推动了地理信息系统向更高层次的应用发展。 ... [详细]
  • 本文详细介绍了如何使用CSS控制文本样式,包括字体加粗、大小、字体类型、行高、背景色、文本对齐方式、字间距、文本装饰等属性,并通过具体示例代码展示这些样式的实际应用。 ... [详细]
  • 使用 Angular CLI 快速构建 Web 前端项目
    本文详细介绍如何利用 Angular CLI 的常用命令来搭建和管理 Angular 项目,包括项目创建、依赖管理、组件生成等核心操作。 ... [详细]
author-avatar
跟我搞对象吧
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有