亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

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

2019-11-19 14:38:47
字體:
來源:轉載
供稿:網友

上篇文章給大家介紹了細說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不會被切割解析!?。?/p>

  這兩種方式只是不同的使用方法,最后的結果都是一樣的。

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、oneOfif (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的規則已經解析完畢,真是配置簡單處理復雜。

總結

以上所述是小編給大家介紹的細說webpack源碼之compile流程-rules參數處理技巧(2),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久久久久一区二区三区| 色香阁99久久精品久久久| 成人精品视频99在线观看免费| 中文字幕日韩精品在线| 久久精品亚洲94久久精品| 亚洲女在线观看| 欧美成人激情视频免费观看| 欧美成人久久久| 国产+成+人+亚洲欧洲| 国产福利精品在线| 美乳少妇欧美精品| 日韩中文字幕视频在线观看| 国产精品女主播视频| 亚洲激情第一页| 日韩高清不卡av| 国产一区二区三区在线观看网站| 欧美在线视频在线播放完整版免费观看| 欧美极度另类性三渗透| 亚洲日本成人女熟在线观看| 亚洲视频在线观看免费| 国产精品久久久久久久久久99| 亚洲精品一区中文| 中文字幕日韩精品在线观看| 久久视频在线直播| 精品美女久久久久久免费| 欧美日韩ab片| 国产精品 欧美在线| 国产成人综合av| 中文字幕日韩有码| 欧美日韩一区二区免费在线观看| 97国产suv精品一区二区62| 久久精品国产欧美亚洲人人爽| 欧美中文字幕精品| 日韩av免费观影| 中文字幕在线观看日韩| 亚洲一区国产精品| 高清欧美一区二区三区| 久久久久女教师免费一区| 久久精品国产精品亚洲| 国产亚洲精品成人av久久ww| 国产精品扒开腿做爽爽爽男男| 欧美一性一乱一交一视频| 97超碰蝌蚪网人人做人人爽| 久久久久久久久久久免费精品| www.日韩欧美| 亚洲精品国产精品国自产观看浪潮| 日产日韩在线亚洲欧美| 精品国产美女在线| 久久久久久91香蕉国产| 国产在线播放91| 久久伊人精品视频| 精品欧美国产一区二区三区| 欧美在线性爱视频| 欧洲中文字幕国产精品| 中文字幕国产日韩| 国产免费成人av| 日韩美女免费视频| 日韩精品极品视频免费观看| 不卡av日日日| 欧美色欧美亚洲高清在线视频| 萌白酱国产一区二区| 久久久久久国产免费| 亚洲成人精品视频| 亚洲欧美综合精品久久成人| 日韩欧美中文字幕在线观看| 韩国三级日本三级少妇99| 91九色蝌蚪国产| 亚洲精品国偷自产在线99热| 久久精品国产96久久久香蕉| 亚洲福利在线看| 最近2019好看的中文字幕免费| 国产精品极品美女粉嫩高清在线| 国产精品久久久久久久久粉嫩av| 亚洲色图综合网| 午夜精品久久久久久99热| 亚洲精品网站在线播放gif| 国产剧情久久久久久| 欧美国产精品人人做人人爱| 日韩精品在线观看视频| 欧美日韩一区免费| 久久艹在线视频| 亚洲精品成人久久久| 精品国产欧美一区二区三区成人| 欧美午夜精品伦理| 国产精品视频一区二区三区四| 自拍偷拍亚洲欧美| 97人人模人人爽人人喊中文字| 日韩大片在线观看视频| 国产精品高潮粉嫩av| 亚洲丁香婷深爱综合| 欧美精品videosex极品1| 亚洲风情亚aⅴ在线发布| 久久精品国产欧美激情| 亚洲天堂视频在线观看| 国产日韩一区在线| 国产精品精品视频一区二区三区| 亚洲视频在线观看| 97热精品视频官网| 大荫蒂欧美视频另类xxxx| 国内精品久久久久影院优| 乱亲女秽乱长久久久| 亚洲毛片在线观看.| 在线成人一区二区| 欧美猛男性生活免费| 色香阁99久久精品久久久| 日韩电影免费观看在线观看| 欧美激情视频网址| 国产精品色婷婷视频| 中文字幕欧美亚洲| 992tv成人免费影院| 亚洲全黄一级网站| 社区色欧美激情 | 国产成人av网| 国产亚洲精品久久| 中文字幕一区日韩电影| 欧美电影免费在线观看| 亚洲人成电影在线播放| 色婷婷综合久久久久中文字幕1| 国产女人18毛片水18精品| 国产精品老女人视频| 国产在线播放91| 亚洲欧美一区二区激情| 精品成人久久av| 日韩av中文字幕在线免费观看| 国产精品一区二区久久久| 亚州av一区二区| 欧美插天视频在线播放| 国产91在线播放| 亚洲电影免费观看高清完整版在线观看| 日韩av色综合| 日本精品视频网站| 成人免费视频在线观看超级碰| 俺去亚洲欧洲欧美日韩| 国产精品尤物福利片在线观看| 亚洲网站视频福利| 久久久久久com| 国产欧美久久一区二区| 国产不卡一区二区在线播放| 国产精品视频一区二区高潮| 日韩电影在线观看永久视频免费网站| 在线日韩中文字幕| 国产精品91久久| 亚洲wwwav| 亚洲欧美国产日韩中文字幕| 日韩av一区在线观看| 久久视频中文字幕| 亚洲人成在线免费观看| 日本免费久久高清视频| 欧美激情第三页| 亚洲大胆美女视频| 欧美韩国理论所午夜片917电影| 久久久午夜视频| 亚洲国产又黄又爽女人高潮的| 国产精品视频白浆免费视频| 亚洲欧美色图片| 色伦专区97中文字幕| 蜜臀久久99精品久久久无需会员| 欧美在线中文字幕| 国产日产久久高清欧美一区| 国产综合在线看| 欧美成人午夜激情在线| 国产人妖伪娘一区91| 欧美超级乱淫片喷水| 欧美在线激情视频|