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

posted @ 2020-05-06 09:36  漠然0408丶  阅读(1229)  评论(0编辑  收藏  举报