About rules parameter processing in webpack
这篇文章主要介绍了webpack源码之compile流程-rules参数处理技巧的相关知识,需要的朋友参考下吧
上篇文章给大家介绍了细说webpack源码之compile流程-rules参数处理技巧(1), 细说webpack源码之compile流程-入口函数run
大家可以点击查看。
第一步处理rule为字符串,直接返回一个包装类,很简单看注释就好了。
test
然后处理test、include、exclude,如下:
if (rule.test || rule.include || rule.exclude) { // 标记使用参数 checkResourceSource("test + include + exclude"); // 没有就是undefined condition = { test: rule.test, include: rule.include, exclude: rule.exclude }; // 处理常规参数 try { newRule.resource = RuleSet.normalizeCondition(condition); } catch (error) { throw new Error(RuleSet.buildErrorMessage(condition, error)); } }
checkResourceSource直接看源码:
let resourceSource; // ... function checkResourceSource(newSource) { // 第一次直接跳到后面赋值 if (resourceSource && resourceSource !== newSource) throw new Error(RuleSet.buildErrorMessage(rule, new Error("Rule can only have one resource source (provided " + newSource + " and " + resourceSource + ")"))); resourceSource = newSource; }
这个用于检测配置来源的唯一性,后面会能看到作用,同样作用的还有checkUseSource方法。
随后将三个参数包装成一个对象传入normalizeCondition方法,该方法对常规参数进行函数包装:
class RuleSet { constructor(rules) { /**/ }; static normalizeCondition(condition) { // 假值报错 if (!condition) throw new Error("Expected condition but got falsy value"); // 检测给定字符串是否以这个开头 if (typeof condition === "string") { return str => str.indexOf(condition) === 0; } // 函数直接返回 if (typeof condition === "function") { return condition; } // 正则表达式返回一个正则的test函数 if (condition instanceof RegExp) { return condition.test.bind(condition); } // 数组map递归处理 有一个满足返回true if (Array.isArray(condition)) { const items = condition.map(c => RuleSet.normalizeCondition(c)); return orMatcher(items); } if (typeof condition !== "object") throw Error("Unexcepted " + typeof condition + " when condition was expected (" + condition + ")"); const matchers = []; // 对象会对每个值进行函数包装弹入matchers中 Object.keys(condition).forEach(key => { const value = condition[key]; switch (key) { case "or": case "include": case "test": if (value) matchers.push(RuleSet.normalizeCondition(value)); break; case "and": if (value) { const items = value.map(c => RuleSet.normalizeCondition(c)); matchers.push(andMatcher(items)); } break; case "not": case "exclude": if (value) { const matcher = RuleSet.normalizeCondition(value); matchers.push(notMatcher(matcher)); } break; default: throw new Error("Unexcepted property " + key + " in condition"); } }); if (matchers.length === 0) throw new Error("Excepted condition but got " + condition); if (matchers.length === 1) return matchers[0]; return andMatcher(matchers); } }
这里用js的rules做案例,看这个方法的返回:
class RuleSet { constructor(rules) { /**/ }; /* Example: { test: /\.js$/, loader: 'babel-loader', include: [resolve('src'), resolve('test')] } */ /* condition: { test: /\.js$/, include: ['d:\\workspace\\src', 'd:\\workspace\\test'], exclude: undefined } */ static normalizeCondition(condition) { // include返回类似于 [(str) => str.indexOf('d:\\workspace\\src') === 0,...] 的函数 if (typeof condition === "string") { return str => str.indexOf(condition) === 0; } // test参数返回了 /\.js$/.test 函数 if (condition instanceof RegExp) { return condition.test.bind(condition); } // include为数组 if (Array.isArray(condition)) { const items = condition.map(c => RuleSet.normalizeCondition(c)); return orMatcher(items); } const matchers = []; // 解析出['test','include','exclude'] Object.keys(condition).forEach(key => { const value = condition[key]; switch (key) { // 此value为一个数组 case "include": case "test": if (value) matchers.push(RuleSet.normalizeCondition(value)); break; // undefined跳过 case "exclude": if (value) { /**/ } break; default: throw new Error("Unexcepted property " + key + " in condition"); } }); return andMatcher(matchers); } }
这里继续看orMatcher、andMatcher函数的处理:
function orMatcher(items) { // 当一个函数被满足条件时返回true return function(str) { for (let i = 0; i < items.length; i++) { if (items[i](str)) return true; } return false; }; } function andMatcher(items) { // 当一个条件不满足时返回false return function(str) { for (let i = 0; i < items.length; i++) { if (!items[i](str)) return false; } return true; }; }
从字面意思也可以理解函数作用,比如说这里的include被包装成一个orMatcher函数,传入的字符串无论是以数组中任何一个元素开头都会返回true,andMatcher以及未出现的notMatcher同理。
resource
下面是对resource参数的处理,源码如下:
if (rule.resource) { // 如果前面检测到了test || include || exclude参数 这里会报错 checkResourceSource("resource"); try { newRule.resource = RuleSet.normalizeCondition(rule.resource); } catch (error) { throw new Error(RuleSet.buildErrorMessage(rule.resource, error)); } }
可以看出这个参数与前面那个是互斥的,应该是老版API,下面两种方式实现是一样的:
/* 方式1: rules:[ { test: /\.js$/, loader: 'babel-loader', include: [resolve('src'), resolve('test')] } ] */ /* 方式2: rules:[ { resource:{ test: /\.js$/, include: [resolve('src'), resolve('test')] exclude: undefined } } ] */
接下来的resourceQuery、compiler、issuer先跳过,脚手架没有,不知道怎么写。
loader
下面是另一块主要配置loader(s),这里loader与loaders作用一样的,当初还头疼啥区别:
const loader = rule.loaders || rule.loader; // 单loader情况 if (typeof loader === "string" && !rule.options && !rule.query) { checkUseSource("loader"); newRule.use = RuleSet.normalizeUse(loader.split("!"), ident); } // loader配合options或query出现 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); } // options与query同时出现报错 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)"))); } /* 处理这种愚蠢用法时: { test: /\.css$/, loader: [{ loader: 'less-loader' }, { loader: 'css-loader' }] } */ else if (loader) { checkUseSource("loaders"); newRule.use = RuleSet.normalizeUse(loader, ident); } // 单独出现options或者query报错 else if (rule.options || rule.query) { throw new Error(RuleSet.buildErrorMessage(rule, new Error("options/query provided without loader (use loader + options)"))); }
之前举例的babel-loader就是第一种单loader配置,这里使用vue-loader嵌套的css配置作为示例。
首先normalizeUse方法如下:
static normalizeUse(use, ident) { // 单loader字符串 if (Array.isArray(use)) { return use // 如果是单loader情况这里会返回[[loader1...],[loader2...]] .map((item, idx) => RuleSet.normalizeUse(item, `${ident}-${idx}`)) // 扁平化后变成[loader1,loader2] .reduce((arr, items) => arr.concat(items), []); } // 对象或字符串 return [RuleSet.normalizeUseItem(use, ident)]; };
先讲解有options或者query的模式,这里会把参数包装一个对象传入normalizeUse方法:
loader && (options || query)
// indet => 'ref-' static normalizeUseItem(item, ident) { if (typeof item === "function") return item; if (typeof item === "string") { return RuleSet.normalizeUseItemString(item); } const newItem = {}; if (item.options && item.query) throw new Error("Provided options and query in use"); if (!item.loader) throw new Error("No loader specified"); newItem.options = item.options || item.query; // 防止options:null的情况 if (typeof newItem.options === "object" && newItem.options) { // 这里只是为了处理ident参数 if (newItem.options.ident) newItem.ident = newItem.options.ident; else newItem.ident = ident; } // 取出loader参数 const keys = Object.keys(item).filter(function(key) { return ["options", "query"].indexOf(key) < 0; }); keys.forEach(function(key) { newItem[key] = item[key]; }); /* newItem = { loader:'原字符串', ident:'ref-', (或自定义) options:{...} } */ return newItem; }
比起对test的处理,这里就简单的多,简述如下:
1、尝试取出options(query)中的ident参数,赋值给newItem的ident,没有就赋值为默认的ref-
2、取出对象中不是options、query的键,赋值给newItem,这里传进来的键只有三个,剩下的就是loader,这个filter是逗我???
3、返回newItem对象
总之,不知道为什么防止什么意外情况而写出来的垃圾代码,这段代码其实十分简单。
单loader
第二种情况是单字符串,会对'!'进行切割调用map方法处理,再调用reduce方法扁平化为一个数组,直接看normalizeUseItemString方法:
/* Example: { test: /\.css$/, loader: 'css-loader?{opt:1}!style-loader' }
返回:
[{loader:'css-loader',options:{opt:1}}, {loader:'style-loader'}] */ static normalizeUseItemString(useItemString) { // 根据'?'切割获取loader的参数 const idx = useItemString.indexOf("?"); if (idx >= 0) { return { loader: useItemString.substr(0, idx), // 后面的作为options返回 options: useItemString.substr(idx + 1) }; } return { loader: useItemString }; }
这种就是串行调用loader的处理方式,代码很简单。
Tips
这里有一点要注意,一旦loader使用了串行调用方式,不要传options或者query参数,不然loader不会被切割解析!!!
这两种方式只是不同的使用方法,最后的结果都是一样的。
use
下面是use参数的解析,估计因为这是老版的API,所以格式要求严格,处理比较随便:
if (rule.use) { checkUseSource("use"); newRule.use = RuleSet.normalizeUse(rule.use, ident); }
如果不用loader(s),可以用use替代,但是需要按照格式写,比如说上述串行简写的loader,在use中就需要这样写:
/* { test:/\.css$/, user:['css-loader','style-loader] } */
而对应options或query,需要这样写:
/* { test:/\.css$/, user:{ loader:'css-loader', options:'1' } } */
因为基本上不用了,所以这里简单看一下。
rules、oneOf if (rule.rules) newRule.rules = RuleSet.normalizeRules(rule.rules, refs, `${ident}-rules`); if (rule.oneOf) newRule.oneOf = RuleSet.normalizeRules(rule.oneOf, refs, `${ident}-oneOf`);
这两个用得少,也没啥难理解的。
下一步是过滤出没有处理的参数,添加到newRuls上:
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]; });
基本上用到都是test、loader、options,暂时不知道有啥额外参数。
ident
// 防止rules:[]的情况 if (Array.isArray(newRule.use)) { newRule.use.forEach((item) => { // ident来源于options/query的ident参数 if (item.ident) { refs[item.ident] = item.options; } }); }
最后这个地方是终于用到了传进来的纯净对象refs。
如果在options中传了ident参数,会填充这个对象,key为ident值,value为对应的options。
至此,所有rules的规则已经解析完毕,真是配置简单处理复杂。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
The above is the detailed content of About rules parameter processing in webpack. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











Vue is an excellent JavaScript framework that can help us quickly build interactive and efficient web applications. Vue3 is the latest version of Vue, which introduces many new features and functionality. Webpack is currently one of the most popular JavaScript module packagers and build tools, which can help us manage various resources in our projects. This article will introduce how to use Webpack to package and build Vue3 applications. 1. Install Webpack

Differences: 1. The startup speed of the webpack server is slower than that of Vite; because Vite does not require packaging when starting, there is no need to analyze module dependencies and compile, so the startup speed is very fast. 2. Vite hot update is faster than webpack; in terms of HRM of Vite, when the content of a certain module changes, just let the browser re-request the module. 3. Vite uses esbuild to pre-build dependencies, while webpack is based on node. 4. The ecology of Vite is not as good as webpack, and the loaders and plug-ins are not rich enough.

With the continuous development of web development technology, front-end and back-end separation and modular development have become a widespread trend. PHP is a commonly used back-end language. When doing modular development, we need to use some tools to manage and package modules. Webpack is a very easy-to-use modular packaging tool. This article will introduce how to use PHP and webpack for modular development. 1. What is modular development? Modular development refers to decomposing a program into different independent modules. Each module has its own function.

Configuration method: 1. Use the import method to put the ES6 code into the packaged js code file; 2. Use the npm tool to install the babel-loader tool, the syntax is "npm install -D babel-loader @babel/core @babel/preset- env"; 3. Create the configuration file ".babelrc" of the babel tool and set the transcoding rules; 4. Configure the packaging rules in the webpack.config.js file.

As the complexity of modern web applications continues to increase, building excellent front-end engineering and plug-in systems has become increasingly important. With the popularity of Spring Boot and Webpack, they have become a perfect combination for building front-end projects and plug-in systems. SpringBoot is a Java framework that creates Java applications with minimal configuration requirements. It provides many useful features, such as automatic configuration, so that developers can build and deploy web applications faster and easier. W

Usage of Pattern.compile function in Java The Pattern.compile function in Java is a method used to compile regular expressions. Regular expression is a powerful string matching and processing tool that can be used to find, replace, verify strings and other operations. The Pattern.compile function allows us to compile a string pattern into a Pattern object, which can then be used to perform a series of string operations. Pattern.compi

In vue, webpack can package js, css, pictures, json and other files into appropriate formats for browser use; in webpack, js, css, pictures, json and other file types can be used as modules. Various module resources in webpack can be packaged and merged into one or more packages, and during the packaging process, the resources can be processed, such as compressing images, converting scss to css, converting ES6 syntax to ES5, etc., which can be recognized by HTML. file type.

Webpack is a module packaging tool. It creates modules for different dependencies and packages them all into manageable output files. This is especially useful for single-page applications (the de facto standard for web applications today).
