资讯专栏INFORMATION COLUMN

webpack4 SplitChunks实现代码分隔详解

Zachary / 1863人阅读

摘要:代码均放在仓库给我们带来了一些改变。插件以前使用允许我们将公共依赖项提取到现有的或全新的代码块中。代码详情见文章开头仓库。这才是配置的关键缓存组会继承的配置,但是和只能用于配置缓存组。可以通过禁用缓存组。

代码均放在git仓库

Webpack 4给我们带来了一些改变。包括更快的打包速度,引入了SplitChunksPlugin插件来取代(之前版本里的)CommonsChunksPlugin插件。在这篇文章中,你将学习如何分割你的输出代码,从而提升我们应用的性能。

SplitChunks插件(webpack 4.x以前使用CommonsChunkPlugin)允许我们将公共依赖项提取到现有的entry chunk或全新的代码块中。

代码分割的理念

首先搞明白: webpack里的代码分割是个什么鬼? 它允许你将一个文件分割成多个文件。如果使用的好,它能大幅提升你的应用的性能。其原因是基于浏览器会缓存你的代码这一事实。每当你对某一文件做点改变,访问你站点的人们就要重新下载它。然而依赖却很少变动。如果你将(这些依赖)分离成多带带的文件,访问者就无需多次重复下载它们了。

使用webpack生成一个或多个包含你源代码最终版本的“打包好的文件”(bundles),(概念上我们当作)它们由(一个一个的)chunks组成。

首先 webpack 总共提供了三种办法来实现 Code Splitting,如下:

入口配置:entry 入口使用多个入口文件;

抽取公有代码:使用 SplitChunks 抽取公有代码;

动态加载 :动态加载一些代码。

这里我们姑且只讨论使用 SplitChunks 抽取公有代码。

splitChunks配置

在src目录下创建三个文件pageA.js、pageB.js和pageC.js。代码详情见文章开头git仓库。

// src/pageA.js
var react = require("react");
var reactDom = require("react-dom");
var utility1 = require("../utils/utility1");
var utility2 = require("../utils/utility2");
new Vue();

module.exports = "pageA";
// src/pageB.js
var react = require("react");
var reactDom = require("react-dom");
var utility2 = require("../utils/utility2");
var utility3 = require("../utils/utility3");

module.exports = "pageB";
// src/pageC.js
var react = require("react");
var reactDom = require("react-dom");
var utility2 = require("../utils/utility2");
var utility3 = require("../utils/utility3");

module.exports = "pageC";

入口文件 && 出口文件

entry: {
    pageA: "./src/pageA",    // 引用utility1.js  utility2.js
    pageB: "./src/pageB",    // 引用utility2.js  utility3.js
    pageC: "./src/pageC",   // 引用utility2.js  utility3.js
},
output: {
    path: path.join(__dirname, "dist"),
    filename: "[name].[hash:8].bundle.js"
},
配置optimization

首先我们配置optimization如下:

optimization: {
    splitChunks: {
      chunks: "all",
  },

执行npm run build打包命令之后,查看dist目录

可以发现,打包出来的除了三个page文件,还存在一个vendors~pageA~pageB~pageC.[hash].bundle.js文件(此文件中保存了pageA、pageB、pageC和node_modules中共有的size大于30KB的文件)。事实上这全靠了配置中本身默认固有一个cacheGroups的配置项:

splitChunks: {
    chunks: "all",
    cacheGroups: {
      vendors: {
        test: /[/]node_modules[/]/,  // 匹配node_modules目录下的文件
        priority: -10   // 优先级配置项
      },
      default: {
        minChunks: 2,
        priority: -20,   // 优先级配置项
        reuseExistingChunk: true
      }
    }
  }

在默认设置中,会将 node_mudules 文件夹中的模块打包进一个叫 vendors的bundle中,所有引用超过两次的模块分配到 default bundle 中。更可以通过 priority 来设置优先级。

参数说明如下:

chunks:表示从哪些chunks里面抽取代码,除了三个可选字符串值 initial、async、all 之外,还可以通过函数来过滤所需的 chunks;

minSize:表示抽取出来的文件在压缩前的最小大小,默认为 30000

maxSize:表示抽取出来的文件在压缩前的最大大小,默认为 0,表示不限制最大大小;

minChunks:表示被引用次数,默认为1;上述配置commons中minChunks为2,表示将被多次引用的代码抽离成commons。

值得注意的是,如果没有修改minSize属性的话,而且被公用的代码(假设是utilities.js)size小于30KB的话,它就不会分割成一个多带带的文件。在真实情形下,这是合理的,因为(如分割)并不能带来性能确实的提升,反而使得浏览器多了一次对utilities.js的请求,而这个utilities.js又是如此之小(不划算)。

maxAsyncRequests:最大的按需(异步)加载次数,默认为 5;

maxInitialRequests:最大的初始化加载次数,默认为 3;

automaticNameDelimiter:抽取出来的文件的自动生成名字的分割符,默认为 ~;

name:抽取出来文件的名字,默认为 true,表示自动生成文件名;

cacheGroups: 缓存组。(这才是配置的关键)

缓存组会继承splitChunks的配置,但是test、priorty和reuseExistingChunk只能用于配置缓存组。cacheGroups是一个对象,按上述介绍的键值对方式来配置即可,值代表对应的选项。除此之外,所有上面列出的选择都是可以用在缓存组里的:chunks, minSize, minChunks, maxAsyncRequests, maxInitialRequests, name。可以通过optimization.splitChunks.cacheGroups.default: false禁用default缓存组。默认缓存组的优先级(priotity)是负数,因此所有自定义缓存组都可以有比它更高优先级(译注:更高优先级的缓存组可以优先打包所选择的模块)(默认自定义缓存组优先级为0)

现在我们再重新来看一下pageA、pageB、pageC三个js文件,这三个文件中都引入了utility2.js文件,但是此文件size很明显小于30KB,所以这部分公用代码并没有分割出来。如果想要分割出来很简单,只需要:

optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 0
  },

执行npm run build打包命令之后,查看dist目录

显然多了一个pageA~pageB~pageC.[hash].bundle.js文件。查看文件可得知此文件中存储了utility2.js中的代码。如下图所示(借助于webpack-bundle-analyzer插件,详情文章末尾附录)。

上图可以看出,React相关代码均放在了vendors~pageA~pageB~pageC.[hash].bundle.js文件中,如果我们想要抽离出React代码,应该怎么做呐?

splitChunks: {
      chunks: "all",
      cacheGroups: {
        commons: {
          chunks: "initial",
          minChunks: 2,
          name: "commons",
          maxInitialRequests: 5,
          minSize: 0, // 默认是30kb,minSize设置为0之后
                            // 多次引用的utility1.js和utility2.js会被压缩到commons中
        },
        reactBase: {
          test: (module) => {
            return /react|redux|prop-types/.test(module.context);
          }, // 直接使用 test 来做路径匹配,抽离react相关代码
          chunks: "initial",
          name: "reactBase",
          priority: 10,
        }
      }
    },

run build之后如下图所示。

看似非常完美,但是reactBase文件中竟然包含了node_modules,神奇的问题?室友都睡觉了,这键盘声影响不好,明天接着看。

附录

我们再安装一个 webpack-bundle-analyzer,这个插件会清晰的展示出打包后的各个bundle所依赖的模块:

npm i webpack-bundle-analyzer -D

引入:

const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin

使用,在plugins数组中添加即可:

new BundleAnalyzerPlugin()

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7308.html

相关文章

  • webpack4 optimization总结

    摘要:总结默认为,效果就是压缩代码。主要就是根据不同的策略来分割打包出来的。同时分割同步和异步代码推荐。内部的参数可以和覆盖外部的参数。正则匹配文件优先级是否复用存在的匹配规则重写公用的次数重写文件名称强制生成文件名称分隔符号 optimization总结 minimize 默认为true,效果就是压缩js代码。 minimizer 可以自定义UglifyJsPlugin和一些配置,默认的压...

    RyanQ 评论0 收藏0
  • webpack4介绍与总结

    摘要:随着承担地职责越来越大,模块化开发的需求越来越急迫。我们可以把当成是模块化标准的实现方案,但的功能不仅限于此。支持多种模块使用方式,包括的。下面介绍一下在工程中常用的。最后一个的输出就是我们最终要的结果。在文件有值的情况下,是必要的。 由于web应用扩展地得极其迅猛,前端技术也是日新月异,前端的苦不是有多难学,而是我刚学完,这东西就被淘汰了(手动哭脸)。框架方面我们有vue、react...

    yanbingyun1990 评论0 收藏0
  • webpack4配置详解之逐行分析

    摘要:今天就尝试着一起来聊聊吧,旨在帮大家加深理解新手更容易上路,都能从到搭建配置自定属于自己的脚手架,或对已封装好的脚手架有进一步的巩固,接下来苏南会详细讲解中的每一个配置字段的作用部分为新增。 showImg(https://segmentfault.com/img/bVbjmMV?w=1008&h=298); 前言   经常会有群友问起webpack、react、redux、甚至cre...

    dkzwm 评论0 收藏0
  • 带你了解webpack

    摘要:根据依赖关系,按照配置文件把模块函数分组打包成若干个。会随着自身的的修改,而发生变化。只需要在命令行运行时带上参数就搞定一些插件的废除和替换废弃了顶替者用属性变化压缩优化代码分割,下面详解还有一些新的插件,。 1. 前端工程化项目打包历史 前端工程化之前的时代略过 1. 半自动执行脚本来压缩合并文件 自从xmlhttprequest被挖掘出来,网页能够和服务端通讯,js能做的事越来越多...

    senntyou 评论0 收藏0

发表评论

0条评论

Zachary

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<