欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

細說webpack源碼之compile流程-rules參數處理技巧(1)

 更新時間:2017年12月26日 09:36:54   作者:書生小龍  
webpack作為一種流行的打包工具被廣泛應用在web項目的前端工程化構建中。下面通過本文給大家介紹webpack源碼之compile流程-rules參數處理技巧,感興趣的朋友一起看看吧

上篇文章給大家介紹了細說webpack源碼之compile流程-rules參數處理技巧(2), 細說webpack源碼之compile流程-入口函數run

大家可以點擊查看。

Tips:寫到這里,需要對當初的規(guī)則進行修改。在必要的地方,會在webpack.config.js中設置特殊的參數來跑源碼,例如本例會使用module:{rules:[...]}來測試,基本上測試參數均取自于vue腳手架(太復雜的刪掉)。

  下面兩節(jié)的主要流程圖如下:

  在進入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默認為命令執(zhí)行路徑
 // 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那一塊,也是本節(jié)的主要內容。

  從參數可以看出,這里是對module.rules處理的地方,本節(jié)中測試配置添加了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-");
 }
}

  生成了一個純凈對象并調用了本地靜態(tài)方法進行格式化:

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 [];
 }
 }
}

  這里也區(qū)分了數組參數與非數組參數,但是有個小bug。

  看代碼可以很容易猜到,數組與非數組的情況理論上是這樣的:

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

  因為傳非數組,會被包裝成一個數組,所以這種情況屬于單loader配置。

  但是,這樣配置是會報錯的,因為過不了validateSchema的驗證,測試結果如圖:

  這就很尷尬了,提供了非數組形式的處理方式,但是又不通過非數組的校驗,所以這基本上是永遠不會被執(zhí)行的代碼。

  管他的,估計源碼太大,開發(fā)者已經顧不上了。

  下面正式進行格式化階段,源碼整理如下:

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),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!

相關文章

最新評論