Webpack-CodeSplit(靜態文件篇)


何為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如何運行的,以后整理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM