webpack4.0各個擊破(4)—— Javascript & splitChunk

来源:https://www.cnblogs.com/dashnowords/archive/2018/08/29/9545482.html
-Advertisement-
Play Games

作為前端最火的構建工具,是前端自動化工具鏈 最重要的部分 ,使用門檻較高。本系列是筆者自己的學習記錄,比較基礎,希望通過 問題 + 解決方式 的模式,以前端構建中遇到的具體需求為出發點,學習 工具中相應的處理辦法。(本篇中的參數配置及使用方式均基於 ) 本篇摘要: 本篇主要介紹基於 的`splitC ...


目錄

webpack作為前端最火的構建工具,是前端自動化工具鏈最重要的部分,使用門檻較高。本系列是筆者自己的學習記錄,比較基礎,希望通過問題 + 解決方式的模式,以前端構建中遇到的具體需求為出發點,學習webpack工具中相應的處理辦法。(本篇中的參數配置及使用方式均基於webpack4.0版本

本篇摘要:

本篇主要介紹基於webpack4.0splitChunks分包技術。

一. Js模塊化開發

javascript之所以需要打包合併,是因為模塊化開發的存在。開發階段我們需要將js文件分開寫在很多零碎的文件中,方便調試和修改,但如果就這樣上線,那首頁的http請求數量將直接爆炸。同一個項目,別人2-3個請求就拿到了需要的文件,而你的可能需要20-30個,結果就不用多說了。

但是合併腳本可不是“把所有的碎片文件都拷貝到一個js文件里”這樣就能解決的,不僅要解決命名空間衝突的問題,還需要相容不同的模塊化方案,更別提根據模塊之間複雜的依賴關係來手動確定模塊的載入順序了,所以利用自動化工具來將開發階段的js腳本碎片進行合併和優化是非常有必要的。

二. Js文件的一般打包需求

  • 代碼編譯(TSES6代碼的編譯)
  • 腳本合併
  • 公共模塊識別
  • 代碼分割
  • 代碼壓縮混淆

三. 使用webpack處理js文件

3.1 使用babel轉換ES6+語法

babelES6語法的轉換工具,對babel不瞭解的讀者可以先閱讀《大前端的自動化工廠(3)——Babel》一文進行瞭解,babelwebpack結合使用的方法也在其中做了介紹,此處僅提供基本配置:

webpack.config.js:

...  
module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
             loader: 'babel-loader'
          }
        ]
      }
    ]
  },
 ...

.babelrc:

{
    "presets":[
        ["env",{
            "targets":{
                "browsers":"last 2 versions"
            }
        }
        ]],
    "plugins": [
         "babel-plugin-transform-runtime" 
    ]
}

3.2 腳本合併

使用webpack對腳本進行合併是非常方便的,畢竟模塊管理文件合併這兩個功能是webpack最初設計的主要用途,直到涉及到分包和懶載入的話題時才會變得複雜。webpack使用起來很方便,是因為實現了對各種不同模塊規範的相容處理,對前端開發者來說,理解這種相容性實現的方式比學習如何配置webpack更為重要。webpack預設支持的是CommonJs規範,但同時為了擴展其使用場景,webpack在後續的版本迭代中也加入了對ES harmony等其他規範定義模塊的相容處理,具體的處理方式將在下一章《webpack4.0各個擊破(5)—— Module篇》詳細分析。

3.3 公共模塊識別

webpack的輸出的文件中可以看到如下的部分:

/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }

上面的__webpack_require__( )方法就是webpack的模塊載入器,很容易看出其中對於已載入的模塊是有統一的installedModules對象來管理的,這樣就避免了模塊重覆載入的問題。而公共模塊一般也需要從bundle.js文件中提取出來,這涉及到下一節的“代碼分割”的內容。

3.4 代碼分割

1. 為什麼要進行代碼分割?

代碼分割最基本的任務是分離出第三方依賴庫,因為第三方庫的內容可能很久都不會變動,所以用來標記變化的摘要哈希contentHash也很久不變,這也就意味著我們可以利用本地緩存來避免沒有必要的重覆打包,並利用瀏覽器緩存避免冗餘的客戶端載入。另外當項目發佈新版本時,如果第三方依賴的contentHash沒有變化,就可以使用客戶端原來的緩存文件(通用的做法一般是給靜態資源請求設置一個很大的max-age),提升訪問速度。另外一些場景中,代碼分割也可以提供對腳本在整個載入周期內的載入時機的控制能力。

2. 代碼分割的使用場景

舉個很常見的例子,比如你在做一個數據可視化類型的網站,引用到了百度的Echarts作為第三方庫來渲染圖表,如果你將自己的代碼和Echarts打包在一起生成一個main.bundle.js文件,這樣的結果就是在一個網速欠佳的環境下打開你的網站時,用戶可能需要面對很長時間的白屏,你很快就會想到將Echarts從主文件中剝離出來,讓體積較小的主文件先在界面上渲染出一些動畫或是提示信息,然後再去載入Echarts,而分離出的Echarts也可以從速度更快的CDN節點獲取,如果載入某個體積龐大的庫,你也可以選擇使用懶載入的方案,將腳本的下載時機延遲到用戶真正使用對應的功能之前。這就是一種人工的代碼分割。

從上面的例子整個的生命周期來看,我們將原本一次就可以載入完的腳本拆分為了兩次,這無疑會加重服務端的性能開銷,畢竟建立TCP連接是一種開銷很大的操作,但這樣做卻可以換來對渲染節奏的控制和用戶體驗的提升非同步模塊懶載入模塊從巨集觀上來講實際上都屬於代碼分割的範疇。code splitting最極端的狀況其實就是拆分成打包前的原貌,也就是源碼直接上線

3. 代碼分割的本質

代碼分割的本質,就是在“源碼直接上線”“打包為唯一的腳本main.bundle.js”這兩種極端方案之間尋找一種更符合實際場景的中間狀態,用可接受的伺服器性能壓力增加來換取更好的用戶體驗。

4. 配置代碼分割

code-splitting技術的配置和使用方法將在下一小節詳細描述。

5. 更細緻的代碼分割

感興趣的讀者可以參考來自google開發者社區的文章《Reduce JavaScript Payloads with Code Splitting》自行研究。

3.5 代碼混淆壓縮

webpack4中已經內置了UglifyJs插件,當打包模式參數mode設置為production時就會自動開啟,當然這不是唯一的選擇,babel的插件中也能提供代碼壓縮的處理,具體的效果和原理筆者尚未深究,感興趣的讀者可以自行研究。

四. 細說splitChunks技術

4.1 參數說明

webpack4廢棄了CommonsChunkPlugin插件,使用optimization.splitChunksoptimization.runtimeChunk來代替,原因可以參考《webpack4:連奏中的進化》一文。關於runtimeChunk參數,有的文章說是提取出入口chunk中的runtime部分,形成一個單獨的文件,由於這部分不常變化,可以利用緩存。google開發者社區的博文是這樣描述的:

 The runtimeChunk option is also specified to move webpack's runtime into the vendors chunk to avoid duplication of it in our app code.

splitChunks中預設的代碼自動分割要求是下麵這樣的:

  • node_modules中的模塊或其他被重覆引用的模塊

    就是說如果引用的模塊來自node_modules,那麼只要它被引用,那麼滿足其他條件時就可以進行自動分割。否則該模塊需要被重覆引用才繼續判斷其他條件。(對應的就是下文配置選項中的minChunks為1或2的場景)

  • 分離前模塊最小體積下限(預設30k,可修改)

    30k是官方給出的預設數值,它是可以修改的,上一節中已經講過,每一次分包對應的都是服務端的性能開銷的增加,所以必須要考慮分包的性價比。

  • 對於非同步模塊,生成的公共模塊文件不能超出5個(可修改)

    觸發了懶載入模塊的下載時,併發請求不能超過5個,對於稍微瞭解過服務端技術的開發者來說,【高併發】【壓力測試】這樣的關鍵詞應該不會陌生。

  • 對於入口模塊,抽離出的公共模塊文件不能超出3個(可修改)

    也就是說一個入口文件的最大並行請求預設不得超過3個,原因同上。

4.2 參數配置

splitChunks的在webpack4.0以上版本中的用法是下麵這樣的:

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',//預設只作用於非同步模塊,為`all`時對所有模塊生效,`initial`對同步模塊有效
      minSize: 30000,//合併前模塊文件的體積
      minChunks: 1,//最少被引用次數
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',//自動命名連接符
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          minChunks:1,//敲黑板
          priority: -10//優先順序更高
        },
        default: {
          test: /[\\/]src[\\/]js[\\/]/
          minChunks: 2,//一般為非第三方公共模塊
          priority: -20,
          reuseExistingChunk: true
        }
      },
      runtimeChunk:{
          name:'manifest'
      }
    }
  }

4.3 代碼分割實例

註:實例中使用的demo及配置文件已放在附件中。

  • 單頁面應用

    單頁面應用只有一個入口文件,splitChunks的主要作用是將引用的第三方庫拆分出來。從下麵的分包結果就可以看出,node_modules中的第三方引用被分離了出來,放在了vendors-main.[hash].js中。

  • 多頁面應用

    多頁面應用的情形稍顯複雜,以《webpack4:連奏中的進化》一文中的例子進行代碼分割處理,源碼的依賴關係為:

    entryA.js: vue vuex component10k
    entryB.js: vue axios component10k
    entryC.js: vue vuex axios component10k

    經過代碼分割後得到的包如下圖所示:

splitChunks提供了更精確的分割策略,但是似乎無法直接通過html-webpack-plugin配置參數來動態解決分割後代碼的註入問題,因為分包名稱是不確定的。這個場景在使用chunks:'async'預設配置時是不存在的,因為非同步模塊的引用代碼是不需要以<script>標簽的形式註入html文件的。

chunks配置項設置為allinitial時,就會有問題,例如上面示例中,通過在html-webpack-plugin中配置excludeChunks可以去除pageabout這兩個chunk,但是卻無法提前排除vendors-about-page這個chunk,因為打包前無法知道是否會生成這樣一個chunk。這個場景筆者並沒有找到現成的解決方案,對此場景有需求的讀者也許可以通過使用html-webpack-plugin事件擴展來處理此類場景,也可以使用折中方案,就是第一次打包後記錄下新生成的chunk名稱,按需填寫至html-webpack-pluginchunks配置項里。

### 4.4 結果分析

通過Bundle Buddy分析工具或webpack-bundle-analyser插件就可以看到分包前後對於公共代碼的抽取帶來的影響(圖片來自參考文獻的博文):

五. 參考及附件說明

【1】附加中文件說明:

  • webpack.spa.config.js——單頁面應用代碼分割配置實例
  • main.js——單頁面應用入口文件
  • webpack.multi.config.js——多頁面應用代碼分割配置實例
  • entryA.js,entryB.js,entryC.js——多頁面應用的3個入口

【2】參考文獻: 《Reduce JavaScript Payloads with Code Splitting》


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

-Advertisement-
Play Games
更多相關文章
  • MySQL表碎片整理 1. 計算碎片大小 2. 整理碎片 2.1 使用alter table table_name engine = innodb命令進行整理。 2.2 使用pt-online-schema-change工具也能進行線上整理表結構,收集碎片等操作。 2.3 使用optimize ta ...
  • 今天,上海尚學堂大數據培訓班畢業的一位學生去參加易普軟體公司面試,應聘的職位是大數據開發。面試官問了他10個問題,主要集中在Hbase、Spark、Hive和MapReduce上,基礎概念、特點、應用場景等問得多。看來,還是非常註重基礎的牢固。整個大數據開發技術,這幾個技術知識點占了很大一部分。那本 ...
  • rmdir命令用來刪除空目錄 註意:子目錄被刪除之前應該是空目錄。就是說,該目錄中的所有文件必須用rm命令全部,另外,當前工作目錄必須在被刪除目錄之上,不能是被刪除目錄本身,也不能是被刪除目錄的子目錄。 雖然還可以用帶有-r選項的rm命令遞歸刪除一個目錄中的所有文件和該目錄本身,但是這樣做存在很大的 ...
  • Sqlserver UrlEncode ...
  • 聚簇索引是對磁碟上實際數據重新組織以按指定的一個或多個列的值排序的演算法。特點是存儲數據的順序和索引順序一致。一般情況下主鍵會預設創建聚簇索引,且一張表只允許存在一個聚簇索引。 在《資料庫原理》一書中是這麼解釋聚簇索引和非聚簇索引的區別的:聚簇索引的葉子節點就是數據節點,而非聚簇索引的葉子節點仍然是索 ...
  • 本文的題目雖然有點小寫意,卻是純粹的技術分析貼,借用一個火鍋店的故事,探討報表查詢場景下的延遲問題和一點資料庫的特性。很久之前,有一家老字型大小的火鍋店,顧客盈門,生意紅火 ...
  • CSS介紹 CSS(Cascading Style Sheet,層疊樣式表)定義如何顯示HTML元素。 當瀏覽器讀到一個樣式表,它就會按照這個樣式表來對文檔進行格式化(渲染)。 CSS實例 每個CSS樣式由兩個組成部分:選擇器和聲明。聲明又包括屬性和屬性值。每個聲明之後用分號結束 CSS註釋 CSS ...
  • 在項目中,常常遇到一些問題,可以通過CSS來快速解決,比如受到佈局影響會導致內容溢出,這個時候就可以使用CSS換行解決 自動換行: { word-wrap:break-word; } 強制不換行: { white-space:nowrap; } 允許在英文單詞內斷行: { word-break:br ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...