.7-淺析webpack源碼之WebpackOptionsDefaulter模塊

来源:http://www.cnblogs.com/QH-Jimmy/archive/2017/12/13/8034891.html
-Advertisement-
Play Games

WebpackOptionsDefaulter模塊 通過參數檢測後,會根據單/多配置進行處理,本文基於單配置,所以會進行到如下代碼: 模塊的作用是進行預設值的設置,流程圖如下: 進入該模塊: 可以看到,這個模塊的內容是用ES6的新語法寫的,很好理解,因為這個模塊是只是針對webpack的預設設置,所 ...


WebpackOptionsDefaulter模塊

 

  通過參數檢測後,會根據單/多配置進行處理,本文基於單配置,所以會進行到如下代碼:

if (Array.isArray(options)) {
    compiler = new MultiCompiler(options.map(options => webpack(options)));
} else if (typeof options === "object") {
    // TODO webpack 4: process returns options
    // 這裡的處理有部分是為了webpack4.0做準備
    new WebpackOptionsDefaulter().process(options);
    // ...
}

  模塊的作用是進行預設值的設置,流程圖如下:

  

  進入該模塊:

"use strict";

const OptionsDefaulter = require("./OptionsDefaulter");
const Template = require("./Template");

class WebpackOptionsDefaulter extends OptionsDefaulter {
    constructor() {
        super();
        this.set("devtool", false);
        // 大量的this.set...
    }
}
module.exports = WebpackOptionsDefaulter;

  可以看到,這個模塊的內容是用ES6的新語法寫的,很好理解,因為這個模塊是只是針對webpack的預設設置,所以主要功能內容應該都在原型上面,直接進入OptionsDefaulter模塊:

"use strict";

function getProperty(obj, name) { /**/ }

function setProperty(obj, name, value) { /**/ }

class OptionsDefaulter {
    constructor() {
        this.defaults = {};
        this.config = {};
    }

    process(options) {
        // TODO: change this for webpack 4: options = Object.assign({}, options);
        for (let name in this.defaults) {
            switch (this.config[name]) {
                // case...
            }
        }
    }

    set(name, config, def) {
        if (arguments.length === 3) {
            this.defaults[name] = def;
            this.config[name] = config;
        } else {
            this.defaults[name] = config;
            delete this.config[name];
        }
    }
}
module.exports = OptionsDefaulter;

  這個模塊也是用ES6寫的,內容比較簡單,主要內容如下:

1、構造函數定義兩個對象defaults,config

2、兩個原型方法process,set

3、兩個工具方法getProperty,setProperty

  構造函數創建了兩個對象,defaults對象保存了參數的預設值,而config對象保存了某些參數的特殊處理方式。

  由於原型方法依賴於工具方法,所以從工具方法開始講解:

getProperty

// obj就是options
function getProperty(obj, name) {
    // 切割name
    name = name.split(".");
    // 註意這裡是length-1
    for (let i = 0; i < name.length - 1; i++) {
        // 層層賦值
        obj = obj[name[i]];
        // 若obj非對象返回直接返回undefined
        if (typeof obj !== "object" || !obj) return;
    }
    // 返回最後一層的值
    return obj[name.pop()];
}

  這個函數有意思,直接看比較懵逼,需要來個案例:

// obj => {entry:'./inpuit.js',output:{filename:'output.js'}}
// name => output.filename
function getProperty(obj, name) {
    // 切割後得到[output,filename]
    name = name.split(".");
    // 第二次跳出迴圈
    for (let i = 0; i < name.length - 1; i++) {
        // obj => {filename:'output.js'}
        obj = obj[name[i]];
        // 返回了對象這裡就不返回了
        if (typeof obj !== "object" || !obj) return;
    }
    // 註意這裡obj是options.output
    // 所以返回的是options.output.filename
    return obj[name.pop()];
}

  可以看出,這個函數是嘗試獲取對象的某個鍵,鍵的遞進用點來連接,如果獲取失敗返回undefined。

  精妙的函數!避免了多次判斷 obj[key] 是否為undefined,直接用字元串的方式解決了此問題。

setProperty

function setProperty(obj, name, value) {
    name = name.split(".");
    for (let i = 0; i < name.length - 1; i++) {
        // 非對象直接返回undefined
        if (typeof obj[name[i]] !== "object" && typeof obj[name[i]] !== "undefined") return;
        // 設置為對象
        if (!obj[name[i]]) obj[name[i]] = {};
        obj = obj[name[i]];
    }
    // 設置對應的值
    obj[name.pop()] = value;
}

  有了前面的getProperty,這個就比較好懂了。

 

  下麵就是原型方法:

set

    set(name, config, def) {
        if (arguments.length === 3) {
            this.defaults[name] = def;
            this.config[name] = config;
        }else {
            this.defaults[name] = config;
            delete this.config[name];
        }
    }

  沒什麼好講的,根據傳參數量對構造函數生成的對象進行複製。

  至於process方法會在後面調用,所以這裡暫時不講,回到WebpackOptionsDefaulter中的大量set,抽取兩個情況的進行講解:

this.set("output.chunkFilename", "make", (options) => {
    const filename = options.output.filename;
    return filename.indexOf("[name]") >= 0 ? filename.replace("[name]", "[id]") : "[id]." + filename;
});
this.set("resolve.extensions", [".js", ".json"]);

  調用這兩個方法後,defaults與config對象如下:

defaults = {
    "resolve.extensions": [".js", ".json"],
    "output.chunkFilename": (options) => {
        const filename = options.output.filename;
        return filename.indexOf("[name]") >= 0 ? filename.replace("[name]", "[id]") : "[id]." + filename;
    })
}
config = {
    "output.chunkFilename": "make"
}

  函數中,由於output.filename是必傳參數,所以能取到值。

  chunkFilename的函數會對字元串中的 [name] 置換成 [id] ,如果沒有就加上 [id.] 首碼。

  例如多輸出中經常會取 [name].js 作為輸出文件名,在這裡chunkFilename的預設值就是 [id].js 。

 

  大量大量的set後,可以來看看process函數了,為方便展示,寫成function形式:

// 傳進來的options
function process(options) {
    // 遍歷defaults對象
    for (let name in this.defaults) {
        // 匹配config參數
        switch (this.config[name]) {
            // 預設情況直接進行賦值
            case undefined:
                if (getProperty(options, name) === undefined)
                    setProperty(options, name, this.defaults[name]);
                break;
                // 用來保證根鍵為對象
            case "call":
                setProperty(options, name, this.defaults[name].call(this, getProperty(options, name), options), options);
                break;
                // 預設值通過調用函數註入
                // 傳入一個options參數
            case "make":
                if (getProperty(options, name) === undefined)
                    setProperty(options, name, this.defaults[name].call(this, options), options);
                break;
                // 將預設值添加進已有的數組中
            case "append":
                {
                    let oldValue = getProperty(options, name);
                    if (!Array.isArray(oldValue)) oldValue = [];
                    oldValue.push.apply(oldValue, this.defaults[name]);
                    setProperty(options, name, oldValue);
                    break;
                }
            default:
                throw new Error("OptionsDefaulter cannot process " + this.config[name]);
        }
    }
}

  根據config的情況有4種預設值註入方式,其中函數調用方式可以更加靈活的進行配置。

 

  為求完整,在某些set中有調用 Template.toIdentifier 方法,看一眼其內部實現:

// 匹配所有非大小寫字母$_
const IDENTIFIER_NAME_REPLACE_REGEX = /^[^a-zA-Z$_]/;
// 匹配所有非大小寫字母數字$_
const IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX = /[^a-zA-Z0-9$_]/g;
module.exports = class Template extends Tapable {
    constructor(outputOptions) { /**/ }

    //靜態方法 直接調用
    static toIdentifier(str) {
        if (typeof str !== "string") return "";
        // 特殊符號全部置換為_
        return str.replace(IDENTIFIER_NAME_REPLACE_REGEX, "_").replace(IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX, "_");
    }

    // 其餘方法...
}

  只是一個普通的字元替換函數而已。

  一句話總結:WebpackOptionsDefaulter模塊對options配置對象添加了大量的預設參數。

 

完事~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • jQuery 的動畫方法(animate)支持各種屬性的過渡,但是預設並不支持色彩的過渡,該插件正是來補足這一點! PS: 該插件支持 RGBA 顏色的過渡,但是請註意,IE8以下的版本不支持 RGBA 顏色。 color backgroundColor borderColor borderBott ...
  • 眾所周知,JavaScript預設採用 進行編碼,允許使用 形式表示一個字元,其中 是字元的Unicode碼點。ES6擴展了unicode的表示 1.字元串新增方法 String.codePointAt(index):正確處理4個位元組存儲的字元,返回一個字元的碼點; String.fromCodeP ...
  • [1]迭代器實現 [2]迭代器分類 [3]迭代類數組 [4]倒序迭代器 [5]中止迭代器 [6]文件上傳 ...
  • 今天把用ionic做一個案例,和ionic示例項目差不多,只是用requirejs分離了controller,但是一直報錯 Error: [ng:areq] Argument ‘AppCtrl’ is not a function, got undefined 經過一番折騰還是沒能解決。後來我細分析 ...
  • 除法不可用手工演算法來計算,其基本思想是反覆做減法,看從被除數裡面最多能減去多少個除數,商就是多少。 除法函數: 如果前者絕對值小於後者直接返回零 做減法時,不需要一個一個減,可以以除數*10^n為基數來減 如果喜歡我的文章,可以掃描二維碼關註我的微信公眾號 爭取每天都分享一點我自己的開發和練習體驗~ ...
  • 用到一些封裝好的運動函數,主要是定時器 效果為圖片和圖片的描述定時自動更換 commom.js tween.js ...
  • mvc/mvvm "阮大神博客" mvc 分為三層,其實M層是數據模型層,它是真正的後端數據在前端js中的一個映射模型,他們的關係是:數據模型層和視圖層有映射關係,model改變,view展示也會更改,當view產生用戶操作或會反饋給controller,controller更改model,這個時候 ...
  • 1.首先先要下載artTemplate.js,這個可以在官網下載也可以在GitHob進行下載。 2.現在是編寫一個存放html標簽的編寫模板,使用<script type="text/html" id="site_template"></script>裝載,裡面的id是你到時候將確認將數據傳給誰的綁 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...