何為CodeSplitting?
webpack從入口文件開始遍歷,找到所有依賴文件,然后打包成最終的一個文件,即bundle.js文件,這是我們經常使用的方式,當一個項目慢慢變得復雜的時候會導致這個bundle.js文件越來越大,瀏覽器加載的速度也會越來越慢,這個過程還不排除我們需要引用的第三方文件,這樣每次無論是構建,還是瀏覽器加載這個最終文件,都會存在效率問題,webpack提供了codesplitting功能來解決這個問題,這可以最大限度的減少瀏覽器加載必要代碼時間(比如首屏渲染優化)。這個過程我們可以分為兩種情況來討論,第三方的分為靜態文件處理,瀏覽器加載必要文件作為動態文件處理(按需加載,懶加載)
具體方案參考“webpack分離第三方解決方案“,這里我們僅僅對optimization.splitChunks分離進行闡述。
先看一個簡單例子
demo.js
import $ from "jquery";
webpack.js
module.exports = { mode: "development", entry: { app: "./demo.js", }, output: { path: path.resolve(__dirname, "./build/"), filename: "[name]-[chunkhash].js", }, devtool: "source-map", }
構建結果

添加chunk,並指定加載第三方類庫
entry: { app: "./demo.js", vendor: ["jquery"], },
構建結果

然后會發現,vendor跟app中均出現了第三方的代碼,明顯不能滿足我們的需求,改進如下
optimization: { splitChunks: { chunks: "all", cacheGroups: { default: false, vendors: false, vendors: { test: /[\\/]node_modules[\\/]/, name: "vendor", priority: -10, chunks: "all", reuseExistingChunk: true, enforce: true, }, }, } },
這里通過正則從node_modules里面獲取第三方類庫,然后通過業務代碼檢測引用過哪些第三方,最后將引用的第三方文件打包到vendor.js中
構建結果

app文件結構
(function(modules) { // webpackBootstrap /******/ // install a JSONP callback for chunk loading /******/ function webpackJsonpCallback(data) { /******/ var chunkIds = data[0]; /******/ var moreModules = data[1]; /******/ var executeModules = data[2]; /******/ /******/ // add "moreModules" to the modules object, /******/ // then flag all "chunkIds" as loaded and fire callback /******/ var moduleId, chunkId, i = 0, resolves = []; /******/ for(;i < chunkIds.length; i++) { /******/ chunkId = chunkIds[i]; /******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) { /******/ resolves.push(installedChunks[chunkId][0]); /******/ } /******/ installedChunks[chunkId] = 0; /******/ }
vendor文件結構
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["vendor"],{ /***/ "taue": /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__; ( function( global, factory ) { "use strict"; if ( true && typeof module.exports === "object" ) { module.exports = global.document ? factory( global, true ) : function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { factory( global ); } // Pass this if window is not defined yet } )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
可以看出將第三方跟運行時代碼打到了一起,網上查看以前webpack版本會發現,如果修改業務邏輯,會導致運行代碼改變,從而改變打包出來的vendor.js,但是在這里我試過,並沒有發生改變,而且查看運行時的代碼,在業務邏輯中,並不在vendor中。
原因有三種(查明原因更新此文章)
1.webpack本身對運行代碼做了優化,降低了運行代碼與加載類庫的耦合性從而保證了運行代碼的獨立性。(可能性很大)
2.類庫的掛載形式不同導致了運行時不一樣(可能性不大)
3.demo寫的有局限性
具體原因等查明再更新該文章。在這里留個疑問。
然后我們如何避免運行時代碼的改變,我們要提取運行時的代碼,改進如下
optimization: { splitChunks: { chunks: "all", cacheGroups: { default: false, vendors: false, vendors: { test: /[\\/]node_modules[\\/]/, name: "vendor", priority: -10, chunks: "all", reuseExistingChunk: true, enforce: true, }, }, }, runtimeChunk: { name: "manifest", }, }
構建結果

我們可以檢查下manifest.js代碼
(function(modules) { // webpackBootstrap /******/ // install a JSONP callback for chunk loading /******/ function webpackJsonpCallback(data) { /******/ var chunkIds = data[0]; /******/ var moreModules = data[1]; /******/ var executeModules = data[2]; /******/ /******/ // add "moreModules" to the modules object, /******/ // then flag all "chunkIds" as loaded and fire callback /******/ var moduleId, chunkId, i = 0, resolves = [];
保留了運行時的代碼
查看業務代碼
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["app"],{ /***/ "fhko": /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! jquery */ "taue"); /* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__);
已經沒有了運行時的代碼。
總結
到這里其實我們已經看到了webpack加載模塊代的方式了。大致流程就是將模塊代碼放入了window["webpackJsonp"],window["webpackJsonp"]是數組類型,數組中的每一項為一個chunk文件
chunk又包含了chunk[0]依賴文件名(可多個),chunk文件內容。然后運行文件也就是manifest遍歷這個數組,然后挨個執行目標函數,我們在回憶下怎么調用的。
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []; /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); /******/ jsonpArray.push = webpackJsonpCallback; /******/ jsonpArray = jsonpArray.slice(); /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]); /******/ var parentJsonpFunction = oldJsonpFunction;
好了針對靜態的code split的總結就這些,具體細節可以查看官方文檔,別看中文的,已經不更新了。關於webpack如何運行的,以后整理。
