您现在的位置是:网站首页> 编程资料编程资料
splitChunks精细控制代码分割降低包大小_javascript技巧_
2023-05-24
441人已围观
简介 splitChunks精细控制代码分割降低包大小_javascript技巧_
背景
前端小伙伴都知道,为了降低包大小,经常会把依赖的前端模块独立打包,比如把 vue、vue-router 打到一个单独的包 vendor 中。另外,常会将存在多个路由的复杂页面的每个页面都单独打一个包,只有访问某个页面的时候,再去下载该页面的js包,以此来加快首页的渲染。
无论是 react 还是 vue 都提供了完善的工具,帮我们屏蔽了繁琐的配置工作。当我们对代码进行构建时,已经自动帮我们完成了代码的拆分工作。
所以,很多小伙伴并不知道背后到底发生了什么事。至于为什么这么拆分,到底如何控制代码的拆分,更是一头雾水了。
问题测验
讲解开始之前,大家先看一个问题。如果你已经知道问题的答案,而且明白为什么,就不必往下阅读了。如果不知道答案或者知道答案,但不知道原因。那么,强烈建议阅读本文。
// webpack.config.js const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: { app: "./src/index.js" }, output: { filename: "[name].js", path: path.resolve(__dirname, "dist") }, optimization: { splitChunks: { chunks: "all" } }, plugins: [ new HtmlWebpackPlugin() ] }; // index.js import "vue" import(/*webpackChunkName: 'a' */ "./a"); import(/*webpackChunkName: 'b' */ "./b");
// a.js import "vue-router"; import "./someModule"; // 模块大小大于30kb
// b.js import "vuex"; import "./someModule"; // 模块大小大于30kb
// someModule.js // 该模块大小超过30kb // ...
代码分割的三种方式
webpack 中以下三种常见的代码分割方式:
- 入口起点:使用
entry配置手动地分离代码。 - 动态导入:通过模块的内联函数调用来分离代码。
- 防止重复:使用
splitChunks去重和分离 chunk。 第一种方式,很简单,只需要在entry里配置多个入口即可:
entry: { app: "./index.js", app1: "./index1.js" } 第二种方式,就是在代码中自动将使用 import() 加载的模块分离成独立的包:
//... import("./a"); //... 第三种方式,是使用 splitChunks 插件,配置分离规则,然后 webpack 自动将满足规则的 chunk 分离。一切都是自动完成的。
前两种拆分方式,很容易理解。本文主要针对第三种方式进行讨论。
splitChunks 代码拆分
splitChunks 默认配置
splitChunks: { // 表示选择哪些 chunks 进行分割,可选值有:async,initial和all chunks: "async", // 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb。 minSize: 30000, // 表示一个模块至少应被minChunks个chunk所包含才能分割。默认为1。 minChunks: 1, // 表示按需加载文件时,并行请求的最大数目。默认为5。 maxAsyncRequests: 5, // 表示加载入口文件时,并行请求的最大数目。默认为3。 maxInitialRequests: 3, // 表示拆分出的chunk的名称连接符。默认为~。如chunk~vendors.js automaticNameDelimiter: '~', // 设置chunk的文件名。默认为true。当为true时,splitChunks基于chunk和cacheGroups的key自动命名。 name: true, // cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块,就分配到该组。模块可以被多个组引用,但最终会根据priority来决定打包到哪个组中。默认将所有来自 node_modules目录的模块打包至vendors组,将两个以上的chunk所共享的模块打包至default组。 cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, // default: { minChunks: 2, priority: -20, reuseExistingChunk: true } } } 以上配置,概括如下4个条件:
- 模块在代码中被复用或者来自
node_modules文件夹 - 模块的体积大于等于30kb(压缩之前)
- 当按需加载 chunks 时,并行请求的最大数量不能超过5
- 初始页面加载时,并行请求的最大数量不能超过将3
// index.js import("./a"); // ... // a.js import "vue"; // ...
以上代码,在默认配置下的构建结果如下:

原因分析:
index.js作为入口文件,属于入口起点手动配置分割代码的情况,因此会独立打包。(app.js)a.js通过import()进行加载,属于动态导入的情况,因此会独立打出一个包。(1.js)vue来自node_modules目录,并且大于30kb;将其从a.js拆出后,与a.js并行加载,并行加载的请求数为2,未超过默认的5;vue拆分后,并行加载的入口文件并无增加,未超过默认的3。vue也符合splitChunks的拆分条件,单独打了一个包(2.js)
理解 chunks
chunks 用以告诉 splitChunks 的作用对象,其可选值有 async、 initial 和 all。默认值是 async,也就是默认只选取异步加载的chunk进行代码拆分。这个我们在开头的例子里已经验证。这里我们通过两个例子来看一下当chunks的值为 initial 和 all 时,打包结果如何。 首先将chunks值改为 initial:
chunks: "initial"
构建结果如下:

原因分析:
当 chunks 值为 initial 时,splitChunks 的作用范围变成了非异步加载的初始 chunk,例如我们的 index.js 就是初始化的时候就存在的chunk。而 vue 模块是在异步加载的chunk a.js 中引入的,所以并不会被分离出来。
chunks 仍使用 initial, 我们对 index.js 和 a.js 稍作修改:
// index.js import 'vue' import('./a') // a.js console.log('a') 构建结果如下:

原因分析:
vue 在 index.js 直接被引入,而 index.js 是初始chunk,所以分离出来打到了 vendors~app.js 中。
能不能让 splitChunks 既处理初始chunk也处理异步chunk呢?答案是可以,只需要将 chunks 改为 all :
chunks: "all"
对 index.js 和 a.js 稍作修改:
// index.js import 'vue-router' import('./a') // a.js import 'vue' console.log('a') 构建结果如下:

原因分析:
chunks 值为 all 时,splitChunks 的处理范围包括了初始chunk和异步chunk两种场景,因此 index.js 中的 vue-router 被分拆到了 vendors~app.js 中,而异步加载的chunk a.js 中的 vue 被分拆到了 3.js 中。推荐在开发中将 chunks 设置为 all。
理解 maxInitialRequests
maxIntialRequests 表示 splitChunks 在拆分chunk后,页面中需要请求的初始chunk数量不超过指定的值。所谓初始chunk,指的是页面渲染时,一开始就需要下载的js,区别于在页面加载完成后,通过异步加载的js。
对 splitChunks 做以下修改,其他使用默认配置:
chunks: 'initial', maxInitialRequests: 1
对 index.js 稍作修改:
// index.js import 'vue'
构建结果如下:

原因分析:
因为 maxInitialRequests 为1,如果 vue 从 index.js 中拆出的话,新创建的chunk作为初始chunk index.js 的前置依赖,是需要在页面初始化的时候就先请求的。那么初始化时的请求数变成了2,因此不满足拆分条件,所以 splitChunks 没有对 index.js 进行拆分。
理解 maxAsyncRequests
与 maxInitialRequests 相对,maxAsyncRequests 表示 splitChunks 在拆分chunk后,并行加载的异步 chunk 数不超过指定的值。
对 splitChunks 做以下修改,其他使用默认配置:
maxAsyncRequests: 1
对 index.js 稍作修改:
// index.js import('./a') // a.js import 'vue' console.log('a') 构建结果如下:

原因分析: 因为 maxAsyncRequests 为1,由于 a.js 是通过 import() 异步加载的,此时并行的异步请求数是1。如果将 vue 从 a.js 中拆出的话,拆出的包也将成为一个异步请求chunk。这样的话,当异步请求 a.js 的时候,并行请求数有2个。因此,不满足拆分条件,所以 splitChunks 没有对 a.js 进行拆分。
理解 minChunks
minChunks 表示一个模块至少应被指定个数的 chunk 所共享才能分割。默认为1。
对 splitChunks 做以下修改,其他使用默认配置:
chunks: 'all', minChunks: 2
对 index.js 稍作修改:
// index.js import 'vue'
构建结果如下:

原因分析:
因为 minChunks 为 2,所以只有当 vue 至少被2个 chunk 所共享时,才会被拆分出来。
思考题
请问如下代码,构建结果是什么?
chunks: 'all', minChunks: 2
// index.js import 'vue' import './a'
// a.js import 'vue' console.log('a') 理解 cache groups
cacheGroups 继承 splitChunks 里的所有属性的值,如 chunks、minSize、minChunks、maxAsyncRequests、提示:
本文由神整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!
相关内容
- Node.js数据流Stream之Duplex流和Transform流用法_node.js_
- 微信小程序弹窗组件使用详解_javascript技巧_
- vue3 keepalive源码解析解决线上问题_vue.js_
- 微信小程序原生自定义弹窗效果_javascript技巧_
- vue+elementUi实现点击地图自动填充经纬度以及地点_vue.js_
- Node.js数据流Stream之Readable流和Writable流用法_node.js_
- vue+elementui实现动态添加行/可编辑的table_vue.js_
- typescript+vite项目配置别名的方法实现_vue.js_
- Typescript中使用引用路径别名报错的解决方法_javascript技巧_
- typescript在node.js下使用别名(paths)无效的问题详解_javascript技巧_
