真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

webpackloader配置全流程詳解

前言

1.主要目的為稍微梳理從配置到裝載的流程。另外詳解當(dāng)然要加點源碼提升格調(diào)(本人菜鳥,有錯還請友善指正)

我們一直強調(diào)網(wǎng)站制作、成都網(wǎng)站設(shè)計對于企業(yè)的重要性,如果您也覺得重要,那么就需要我們慎重對待,選擇一個安全靠譜的網(wǎng)站建設(shè)公司,企業(yè)網(wǎng)站我們建議是要么不做,要么就做好,讓網(wǎng)站能真正成為企業(yè)發(fā)展過程中的有力推手。專業(yè)網(wǎng)站制作公司不一定是大公司,創(chuàng)新互聯(lián)公司作為專業(yè)的網(wǎng)絡(luò)公司選擇我們就是放心。

2.被的WebPack打包的文件,都被轉(zhuǎn)化為一個模塊,比如import './xxx/x.jpg'或require('./xxx/x.js')。至于具體實際怎么轉(zhuǎn)化,交由裝載機處理

3.下文會使用打字稿(勸退警告?)以方便說明有哪些選項和各個選項的值類型

配置語法解析

模塊屬性

module.exports = {
    ...
    module: {
        noParse: /jquery/,
        rules: [
            {
                test: /\.js/,
                exclude: /node_modules/,
                use:[
                    {
                        loader: './loader1.js?num=1',
                        options: {myoptions:false},
                    },
                    "./loader2.js?num=2",
                ]
            },
            {
                test: /\.js/,
                include: /src/,
                loader: './loader1.js!./loader2.js',
            },
        ]
    }
}

上述是展示常見的配置寫法.webpack為其選項都編寫了打字稿聲明,這個模塊屬性的聲明在的WebPack /聲明中可見:

export interface ModuleOptions {
    // 一般下面這兩個
    noParse?: RegExp[] | RegExp | Function | string[] | string;
    rules?: RuleSetRules;
    
    // 這些...已被廢棄,即將被刪除,不用看
    defaultRules?: RuleSetRules;
    exprContextCritical?: boolean;
    exprContextRecursive?: boolean;
    exprContextRegExp?: boolean | RegExp;
    exprContextRequest?: string;
    strictExportPresence?: boolean;
    strictThisContextOnImports?: boolean;
    unknownContextCritical?: boolean;
    unknownContextRecursive?: boolean;
    unknownContextRegExp?: boolean | RegExp;
    unknownContextRequest?: string;
    unsafeCache?: boolean | Function;
    wrappedContextCritical?: boolean;
    wrappedContextRecursive?: boolean;
    wrappedContextRegExp?: RegExp;
}

noParse 用于讓的WebPack跳過對這些文件的轉(zhuǎn)化,也就是他們不會被加載程序所處理(但還是會被打包并輸出到DIST目錄)

rules 核心配置,見下文

module.rules屬性

module.rules類型是RuleSetRule[],請繼續(xù)的WebPack /聲明查看其打字稿,有哪些屬性,屬性類型一目了然。

注意RuleSetConditionsRecursive這個東西在另外一個文件聲明,是interface RuleSetConditionsRecursive extends Array {},其實就是export type RuleSetConditionsRecursive = RuleSetCondition[];,代表一個RuleSetCondition數(shù)組

意義直接貼中文文檔:模塊。

好了,上面基本是搬運打字稿聲明,結(jié)合文檔基本能知道有哪些屬性,屬性的類型和含義。下面結(jié)合源碼對文檔一些難以理解的地方補充說明。

 正文

規(guī)則集

規(guī)則的規(guī)范化(類型收斂)

由上可知一個規(guī)則對象,其屬性類型有多種可能,所以應(yīng)該對其規(guī)范化,底層減少代碼的大量typeof等判斷。這是由RuleSet.js進(jìn)行規(guī)范化的。下面是經(jīng)過規(guī)則集處理后的一個規(guī)則對象大致形式:

// rule 對象規(guī)范化后的形狀應(yīng)該是:
{
resource: function(),
resourceQuery: function(),
compiler: function(),
issuer: function(),
use: [
{
loader: string,
options: string | object, // 源碼的注釋可能是歷史遺留原因,options也可為object類型

} // 下文稱呼這個為use數(shù)組的單個元素為 loader對象,規(guī)范化后它一般只有l(wèi)oader和options屬性
],
rules: [],
oneOf: [],
,
}

rules狀語從句:oneOf的英文用來嵌套的,里面的也是規(guī)范過的規(guī)則對象。

它這里的四個函數(shù)是的WebPack用來判斷是否需要把文件內(nèi)容交給裝載器處理的。如的WebPack遇到了import './a.js',那么rule.resource('f:/a.js')===true時會才把文件交由規(guī)則中指定的裝載機去處理,resourceQuery等同理。

的這里的傳入?yún)?shù)'f:/a.js'就是官網(wǎng)所說的

 條件已經(jīng)兩個輸入值:

 資源:請求文件的絕對路徑。它已經(jīng)根據(jù)resolve規(guī)則解析。issuer :被請求資源(請求的資源)的模塊文件的絕對路徑。是導(dǎo)入時的位置。

首先要做的是把Rule.loader, ,Rule.options(Rule.query已廢棄,但尚未刪除),移動全部到Rule.use數(shù)組元素的對象里。主要這由static normalizeRule(rule, refs, ident)函數(shù)處理,代碼主要是處理各種“簡寫”,把值搬運到裝載器對象,做一些報錯處理,難度不大看一下即可,下面挑它里面的“條件函數(shù)”規(guī)范化來說一說。

Rule.resource規(guī)范化

由上可知這是一個“條件函數(shù)”,它是根據(jù)我們的配置中的test,include,exclude,resource規(guī)范化而生成的源碼180多行中:

if (rule.test || rule.include || rule.exclude) {
    checkResourceSource("test + include + exclude");
    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));
    }
}
if (rule.resource) {
    checkResourceSource("resource");
    try {
        newRule.resource = RuleSet.normalizeCondition(rule.resource);
    } catch (error) {
        throw new Error(RuleSet.buildErrorMessage(rule.resource, error));
    }
}

中文檔說Rule.test的英文Rule.resource.test的簡寫,實際就是這串代碼。

checkResourceSource用來檢查是否重復(fù)配置,即文檔中提到的:你如果提供了一個Rule.test選項對話,就不能再提供Rule.resource

求最后RuleSet.normalizeCondition生成一個“條件函數(shù)”,如下:

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;
    }
    if (condition instanceof RegExp) {
        return condition.test.bind(condition);
    }
    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 = [];
    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);
}

這串代碼主要就是根據(jù)字符串,正則表達(dá)式,對象,功能類型來生成不同的“條件函數(shù)”,難度不大。

notMatcher,orMatcher,andMatcher這三個是輔助函數(shù),看名字就知道了,實現(xiàn)上非常簡單,不貼源碼了。有什么不明白的邏輯,代入進(jìn)去跑一跑就知道了

規(guī)則使用規(guī)范化

我們接下來要把Rule.use給規(guī)范分類中翻譯上面提到的那種形式,即讓裝載機只對象保留loader狀語從句:options這兩個屬性(當(dāng)然,并不是它一定只有這兩個屬性)源碼如下:

static normalizeUse(use, ident) {
    if (typeof use === "function") {
        return data => RuleSet.normalizeUse(use(data), ident);
    }
    if (Array.isArray(use)) {
        return use
            .map((item, idx) => RuleSet.normalizeUse(item, `${ident}-${idx}`))
            .reduce((arr, items) => arr.concat(items), []);
    }
    return [RuleSet.normalizeUseItem(use, ident)];
}
static normalizeUseItemString(useItemString) {
    const idx = useItemString.indexOf("?");
    if (idx >= 0) {
        return {
            loader: useItemString.substr(0, idx),
            options: useItemString.substr(idx + 1)
        };
    }
    return {
        loader: useItemString,
        options: undefined
    };
}
static normalizeUseItem(item, ident) {
    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;
    if (typeof newItem.options === "object" && newItem.options) {
        if (newItem.options.ident) {
            newItem.ident = newItem.options.ident;
        } else {
            newItem.ident = ident;
        }
    }
    const keys = Object.keys(item).filter(function(key) {
        return !["options", "query"].includes(key);
    });
    for (const key of keys) {
        newItem[key] = item[key];
    }
    return newItem;
}

這幾個函數(shù)比較繞,但總體來說難度不大。

這里再稍微總結(jié)幾點現(xiàn)象:

1.loader: './loader1!./loader2',如果在Rule.loader指明了兩個以以上裝載機,那么不可設(shè)置Rule.options,因為不知道該把這個選項傳給哪個裝載機,直接報錯

2.-loader不可省略,如babel!./loader的英文非法的,因為在webpack/lib/NormalModuleFactory.js440行左右,已經(jīng)不再支持這種寫法,直接報錯叫你寫成babel-loader

3.loader: './loader1?num1=1&num2=2'將被處理成{loader: './loader', options: 'num=1&num=2'},以?進(jìn)行了字符串分割,最終處理成規(guī)范化裝載機對象

規(guī)則集規(guī)范化到此結(jié)束,有興趣的可以繼續(xù)圍觀源碼的高管方法和構(gòu)造函數(shù)

裝載機

接下來算是番外,討論各種裝載機如何讀取我們配置的對象。

**屬性在的WebPack的傳遞與處理選項**

首先一個裝載機就是簡單的導(dǎo)出一個函數(shù)即可,比如上面舉例用到的

loader1.js:
module.exports = function (content){
    console.log(this)
    console.log(content)
    return content
}

這個函數(shù)里面的這個被綁定到一個loaderContext(loader上下文)中,官方api:loader API。

直接把這個loader1.js加入到配置文件webpack.config.js里面即可,在編譯時他就會打印出一些東西。

簡單而言,就是在裝載機中,可以我們通過this.query來訪問到規(guī)范化裝載機對象options屬性。比如{loader: './loader1.js', options: 'num1=1&num=2'},那么this.query === '?num1=1&num=2'。

問題來了,這個問號哪里來的如果它是一個對象?

的WebPack通過裝載機的領(lǐng)先者來執(zhí)行裝載機,這個問題可以去loader-runner/lib/LoaderRunner.js,在createLoaderObject函數(shù)中有這么一段:

if (obj.options === null)
    obj.query = "";
else if (obj.options === undefined)
    obj.query = "";
else if (typeof obj.options === "string")
    obj.query = "?" + obj.options;
else if (obj.ident) {
    obj.query = "??" + obj.ident;
}
else if (typeof obj.options === "object" && obj.options.ident)
    obj.query = "??" + obj.options.ident;
else
    obj.query = "?" + JSON.stringify(obj.options);

在以及runLoaders函數(shù)里面的這段:

Object.defineProperty(loaderContext, "query", {
    enumerable: true,
    get: function() {
        var entry = loaderContext.loaders[loaderContext.loaderIndex];
        return entry.options && typeof entry.options === "object" ? entry.options : entry.query;
    }
});

總結(jié)來說,當(dāng)選項存在且是一個對象時,那么this.query就是這個對象;如果選項是一個字符串,那么this.query等于一個問號+這個字符串

多數(shù)裝載機讀取選項的方法

const loaderUtils=require('loader-utils')
module.exports = function (content){
    console.log(loaderUtils.getOptions(this))
    return content
}

借助架utils的讀取那么接下來走進(jìn)loaderUtils.getOptions看看:

const query = loaderContext.query;
if (typeof query === 'string' && query !== '') {
  return parseQuery(loaderContext.query);
}
if (!query || typeof query !== 'object') {
  return null;
}
return query;

這里只復(fù)制了關(guān)鍵代碼,它主要是做一些簡單判斷,對字符串的核心轉(zhuǎn)換在parseQuery上,接著看:

const JSON5 = require('json5');
function parseQuery(query) {
  if (query.substr(0, 1) !== '?') {
    throw new Error(
      "A valid query string passed to parseQuery should begin with '?'"
    );
  }
  query = query.substr(1);
  if (!query) {
    return {};
  }
  if (query.substr(0, 1) === '{' && query.substr(-1) === '}') {
    return JSON5.parse(query);
  }
  const queryArgs = query.split(/[,&]/g);
  const result = {};
  queryArgs.forEach((arg) => {
    const idx = arg.indexOf('=');
    if (idx >= 0) {
      let name = arg.substr(0, idx);
      let value = decodeURIComponent(arg.substr(idx + 1));
      if (specialValues.hasOwnProperty(value)) {
        value = specialValues[value];
      }
      if (name.substr(-2) === '[]') {
        name = decodeURIComponent(name.substr(0, name.length - 2));
        if (!Array.isArray(result[name])) {
          result[name] = [];
        }
        result[name].push(value);
      } else {
        name = decodeURIComponent(name);
        result[name] = value;
      }
    } else {
      if (arg.substr(0, 1) === '-') {
        result[decodeURIComponent(arg.substr(1))] = false;
      } else if (arg.substr(0, 1) === '+') {
        result[decodeURIComponent(arg.substr(1))] = true;
      } else {
        result[decodeURIComponent(arg)] = true;
      }
    }
  });
  return result;
}

使用了json5庫,以及自己的一套參數(shù)的轉(zhuǎn)換。

總結(jié)來說,只要你能確保自己使用的裝載器是通過loader-utils來獲取選項對象的,那么你可以直接給選項寫成如下字符串(inline loader中常用,如import 'loader1?a=1&b=2!./a.js'):

options: "{a: '1', b: '2'}" // 非json,是json5格式字符串,略有出入,請右轉(zhuǎn)百度
options: "list[]=1&list=2[]&a=1&b=2" // http請求中常見的url參數(shù)部分

更多示例可在的WebPack /架utils的中查看


當(dāng)前題目:webpackloader配置全流程詳解
分享地址:http://weahome.cn/article/psohjj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部