.15-淺析webpack源碼之WebpackOptionsApply模塊之插件王中王

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

總體過了一下後面的流程,發現Compiler模塊確實不適合單獨講解,這裡繼續講解後面的代碼: 這行代碼與之前設置options預設值非常相似,但是複雜程度根本不是一個次元的。 這一節只能簡單的看一眼內部到底有多少東西,整理後源碼如下: 這個模塊除去父類引入,其餘插件光頂部引入就有34個,簡直就是插件 ...


  總體過了一下後面的流程,發現Compiler模塊確實不適合單獨講解,這裡繼續講解後面的代碼:

compiler.options = new WebpackOptionsApply().process(options, compiler);

  這行代碼與之前設置options預設值非常相似,但是複雜程度根本不是一個次元的。

  這一節只能簡單的看一眼內部到底有多少東西,整理後源碼如下:

"use strict";

const OptionsApply = require("./OptionsApply");
// ...巨量插件引入

class WebpackOptionsApply extends OptionsApply {
    constructor() {
        super();
    }
    process(options, compiler) {
        let ExternalsPlugin;
        compiler.outputPath = options.output.path;
        compiler.recordsInputPath = options.recordsInputPath || options.recordsPath;
        compiler.recordsOutputPath = options.recordsOutputPath || options.recordsPath;
        compiler.name = options.name;
        compiler.dependencies = options.dependencies;
        // 在預設參數配置中被設置為web
        if (typeof options.target === "string") {
            let JsonpTemplatePlugin;
            let NodeSourcePlugin;
            let NodeTargetPlugin;
            let NodeTemplatePlugin;
            switch (options.target) {
                case "web":
                    JsonpTemplatePlugin = require("./JsonpTemplatePlugin");
                    NodeSourcePlugin = require("./node/NodeSourcePlugin");
                    compiler.apply(
                        new JsonpTemplatePlugin(options.output),
                        new FunctionModulePlugin(options.output),
                        new NodeSourcePlugin(options.node),
                        new LoaderTargetPlugin(options.target)
                    );
                    break;
                    // other case...
                default:
                    throw new Error("Unsupported target '" + options.target + "'.");
            }
        } else if (options.target !== false) {
            options.target(compiler);
        } else {
            throw new Error("Unsupported target '" + options.target + "'.");
        }
        // options.output.library參數處理
        if (options.output.library || options.output.libraryTarget !== "var") { /**/ }
        // options.output.externals參數處理
        if (options.externals) { /**/ }
        let noSources;
        let legacy;
        let modern;
        let comment;
        // options.devtool => sourcemap || source-map
        if (options.devtool && (options.devtool.indexOf("sourcemap") >= 0 || options.devtool.indexOf("source-map") >= 0)) { /**/ }
        // options.devtool => eval
        else if (options.devtool && options.devtool.indexOf("eval") >= 0) { /**/ }
        // 載入模塊並觸發entry-option事件流
        compiler.apply(new EntryOptionPlugin());
        compiler.applyPluginsBailResult("entry-option", options.context, options.entry);
        // 瘋狂載入插件
        compiler.apply( /**/ );
        // options.performance參數處理
        if (options.performance) { /**/ }

        // 繼續載入插件
        compiler.apply(new TemplatedPathPlugin());
        compiler.apply(new RecordIdsPlugin());
        compiler.apply(new WarnCaseSensitiveModulesPlugin());
        // options.performance參數處理
        if (options.cache) { /**/ }
        // 觸發after-plugins
        compiler.applyPlugins("after-plugins", compiler);
        if (!compiler.inputFileSystem) throw new Error("No input filesystem provided");
        // 給compiler.resolvers設置值
        compiler.resolvers.normal = ResolverFactory.createResolver(Object.assign({
            fileSystem: compiler.inputFileSystem
        }, options.resolve));
        compiler.resolvers.context = ResolverFactory.createResolver(Object.assign({
            fileSystem: compiler.inputFileSystem,
            resolveToContext: true
        }, options.resolve));
        compiler.resolvers.loader = ResolverFactory.createResolver(Object.assign({
            fileSystem: compiler.inputFileSystem
        }, options.resolveLoader));
        // 觸發after-resolvers事件流
        compiler.applyPlugins("after-resolvers", compiler);
        return options;
    }
}

module.exports = WebpackOptionsApply;

  這個模塊除去父類引入,其餘插件光頂部引入就有34個,簡直就是插件之王。

  略去具體插件內容,先看流程,父類其實是個介面,啥都沒有:

"use strict";

class OptionsApply {
    process(options, compiler) {}
}
module.exports = OptionsApply;

  接下來是一個唯一的主方法process,總結下流程依次為:

1、根據options.target載入對應的插件,如果配置文件沒有配置該參數,則在WebpackOptionsDefaulter模塊會被自動初始化為web。

2、處理options.output.library、options.output.externals參數

3、處理options.devtool參數

4、載入EntryOptionPlugin插件並觸發entry-option的事件流

5、載入大量插件

6、處理options.performance參數

7、載入TemplatePathPlugin、RecordIdPlugin、WarnCaseSensitiveModulesPlugin插件

8、觸發after-plugins事件流

9、設置compiler.resolvers的值

10、觸發after-resolvers事件流

  如果按類型分,其實只有兩種:載入插件,觸發事件流。

  事件流的觸發類似於vue源碼里的鉤子函數,到特定的階段觸發對應的方法,這個思想在Java的數據結構源碼中也被普通應用。

  模塊中的參數處理如果該參數比較常用,那麼就進行分析,其餘不太常用的就先跳過,按順序依次講解。

  這裡的options經過預設參數模塊的加工,豐富後如下:

{
    "entry": "./input.js",
    "output": {
        "filename": "output.js",
        "chunkFilename": "[id].output.js",
        "library": "",
        "hotUpdateFunction": "webpackHotUpdate",
        "jsonpFunction": "webpackJsonp",
        "libraryTarget": "var",
        "path": "D:\\workspace\\doc",
        "sourceMapFilename": "[file].map[query]",
        "hotUpdateChunkFilename": "[id].[hash].hot-update.js",
        "hotUpdateMainFilename": "[hash].hot-update.json",
        "crossOriginLoading": false,
        "chunkLoadTimeout": 120000,
        "hashFunction": "md5",
        "hashDigest": "hex",
        "hashDigestLength": 20,
        "devtoolLineToLine": false,
        "strictModuleExceptionHandling": false
    },
    "context": "D:\\workspace\\doc",
    "devtool": false,
    "cache": true,
    "target": "web",
    "module": {
        "unknownContextRequest": ".",
        "unknownContextRegExp": false,
        "unknownContextRecursive": true,
        "unknownContextCritical": true,
        "exprContextRequest": ".",
        "exprContextRegExp": false,
        "exprContextRecursive": true,
        "exprContextCritical": true,
        "wrappedContextRegExp": {},
        "wrappedContextRecursive": true,
        "wrappedContextCritical": false,
        "strictExportPresence": false,
        "strictThisContextOnImports": false,
        "unsafeCache": true
    },
    "node": {
        "console": false,
        "process": true,
        "global": true,
        "Buffer": true,
        "setImmediate": true,
        "__filename": "mock",
        "__dirname": "mock"
    },
    "performance": {
        "maxAssetSize": 250000,
        "maxEntrypointSize": 250000,
        "hints": false
    },
    "resolve": {
        "unsafeCache": true,
        "modules": ["node_modules"],
        "extensions": [".js", ".json"],
        "mainFiles": ["index"],
        "aliasFields": ["browser"],
        "mainFields": ["browser", "module", "main"],
        "cacheWithContext": false
    },
    "resolveLoader": {
        "unsafeCache": true,
        "mainFields": ["loader", "main"],
        "extensions": [".js", ".json"],
        "mainFiles": ["index"],
        "cacheWithContext": false
    }
}

  除去entry與output.filename,其餘的參數全部是填充上去的,因為後面的流程會檢測參數,所以這裡先列出來。

 

  這一節先這樣,具體內容後面進行詳細講解。


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

-Advertisement-
Play Games
更多相關文章
  • <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> </html> <!DOCTYPE html> <html> <head> <meta charset="UTF-8 ...
  • Chrome Extension是什麼呢?簡而言之,就是Chrome擴展,它是基於Chrome瀏覽器的,我們可以理解它為一個獨立運行在Chrome瀏覽器下的APP,當然核心編程語言就是JavaScript咯,然後結合HTML以及CSS來開發。重點是,這個“APP”功能強大,可以獨自運行,亦可以與打開... ...
  • 在DOM操作里,createElement是創建一個新的節點,createDocumentFragment是創建一個文檔片段。 網上可以搜到的大部分都是說使用createDocumentFragment主要是因為避免因createElement多次添加到document.body引起的效率問題,比如 ...
  • 在我們平時做的很多網站項目中都會需要繪製各種各樣的二維矢量圖形。比如做城市地下管網的斷面圖、管線管點的坐標位置矢量標識圖、鑽孔位置或地層剖面圖等等。我們有很多種方法來繪製這些矢量圖(vml、canvas、svg等等),下麵我要介紹的是SVG繪圖語言,也是我在做項目中用到比較多的,僅以我的個人實戰經驗 ...
  • zTree 優秀的jquery樹插件,文檔詳細,渲染快 使用方法: 1、引用zTree的js和css文件 2、ztree的html為 需加Class:ztree; 3、初始化樹 後臺介面返回數據示例: 4、加入滑鼠移動到顯示的自定義按鈕 5、文檔地址 http://www.treejs.cn/v3/ ...
  • 自從ES6流行起來,Promise 的使用變得更頻繁更廣泛了,比如非同步請求一般返回一個 Promise 對象,Generator 中 yield 後面一般跟 Promise 對象,ES7中 Async 函數中 await 後面一般也是 Promise 對象,還有更多的 NodeAPI 也會返回 Pr ...
  • 如果覺得設置樣式太麻煩,或者頁面上選中的樣式太複雜,也可以用背景圖去修改樣式<div class=""> <label><input type="radio" name="1"><i class="spot"></i>123456</label> <label><input type="radio" ...
  • 近年來,前端技術的發展迅速,但因為前端知識面龐大,在實際學習當中往往無法理清其中的脈絡。下麵從各種庫、框架、插件的層面上,對前端知識點做一些簡單的梳理。從軟體工程上,將前端分為四個由淺及深的層面或階段。 一、基礎層(瀏覽器原生支持html/css/js) HTML超文本標記語言,用標簽構建網頁的內容 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...