chainWebpack之optimization.splitChunks的cacheGroups缓存组代码分块实践案

2023-10-29

研究了好几天webpack打包,我们项目是vue的高版本,已经没有了webpack.config.js文件了,是直接在vue.config.js里的chainWebpack方法直接配置,这样做法的好处是用户既可以保留webpack的默认配置,又可以通过chainWebpack设置更具针对性的参数,链式的写法正是避免了用户直接面对webpack多而复杂的配置项,想要更专业的配置再去学习webpack更深层的知识,大大降低了webpack使用难度和大多数小项目群体学习成本,一般项目只需要使用默认的配置即可满足要求,项目比较大的时候就需要深入的学习了,下面我们来说说webpack里的分块方法optimization.splitChunks以及缓存组cacheGroups的配置方法;

当我们npm run build进行打包的时候,事实上是根据cacheGroups的默认配置进来分包并包的:

splitChunks: {
    chunks: "all",
    minSize: 30000,//默认值,超过30K才独立分包
    cacheGroups: {
        vendors: {
            test: /[\/]node_modules[\/]/,  // 匹配node_modules目录下的文件
            priority: -10   // 打包优先级权重值
        },
        default: {
            minChunks: 2,
            priority: -20,   // 打包优先级权重值
            reuseExistingChunk: true//遇到重复包直接引用,不重新打包
        }
    }
}

默认配置会将 node_mudules下的模块打包进一个叫 vendors的bundle中,所有引用超过两次(minChunks:2)的模块分配到 default的bundle中,这就是我们打包完常见的xxx.bundle.js类;

但是如果node_mudules都打进一个包,通常vendor.bundle.js会很大,因此不得不抽离一部分依赖成单独块,先看一下常见的简单配置:
vue.config.js:

module.exports = {
    chainWebpack( config ) {
        //process.env.NODE_ENV的值要在.env.development和.env.production文件分别配置,不想判断的直接删除when方法这一行
        config.when( process.env.NODE_ENV==="production",  ( config )=>{
            config.optimization.splitChunks({
                chunks:"all",//initial同步,async异步,all同步或者异步
                automaticNameDelimiter:"-",//打包文件名默认连接符号
                cacheGroups: {
                    elementUI: {
                        name: "chunk-elementUI",
                        priority: 30,
                        test: /[\/]node_modules[\/]_?element-ui(.*)/,
                    },
                    nodesInitial: {
                        name:"chunk-nodesInitial",
                        test: /[\/]node_modules[\/]/,
                        priority: 10,
                        minChunks: 1,
                        chunks: "initial",//仅打包同步引用的依赖
                        reuseExistingChunk: true
                    },
                    nodesAsync: {
                        name:"chunk-nodesAsync",
                        test: /[\/]node_modules[\/]/,
                        priority: 9,
                        minChunks: 1,
                        chunks: "async",//仅打包异步引用的依赖
                        reuseExistingChunk: true
                    },
                    components: {
                        name: "chunk-components",
                        test: resolve('src/components'),
                        minChunks: 2,
                        priority: 5,
                        reuseExistingChunk: true
                    }
                }
            })
            config.optimization.runtimeChunk("single")
        })
    }
}

可以见到上面的配置已经将node_modules里的elementUI与公共组件components抽离成独立块,同步和异步引用的依赖分开打包,运行打包之后感觉还不错,这里要注意的是minSize的值,缓存组里独立打包的块如果小于30k,是不会打包的,所以可以针对文件的大小设置打包最小值minSize,使其成为独立块打包出来,下图是小编项目打包后webpack-bundle-analyzer插件分析出来的结果:在这里插入图片描述
因为本项目文件确实比较多比较大,所以可以看到好几个超过1M的大文件,原来默认的时候app.a900c452.js是2M多的,按上面的设置了以后,大于1M的只剩3个,已经是不小的进步,但是明显还可以再抽离一部分,于是在再对webpack-bundle-analyzer插件图分析,看哪些还可以分割出来的,同步异步那两个文件再做分割:

vue.config.js:

module.exports = {
    chainWebpack( config ) {
        //process.env.NODE_ENV的值要在.env.development和.env.production文件分别配置,不想判断的直接删除when方法这一行
        config.when( process.env.NODE_ENV==="production",  ( config )=>{
            config.optimization.splitChunks({
                chunks:"all",//initial同步,async异步,all同步或者异步
                automaticNameDelimiter:"-",
                cacheGroups: {
                    elementUI: {
                        name: "chunk-elementUI",
                        priority: 40,
                        test: /[\/]node_modules[\/]_?element-ui(.*)/,
                        minSize:0
                    },
                    formMaking: {
                        name:"chunk-formMaking",
                        test: resolve('lib/vue-form-making/dist'),
                        priority: 39,
                        minSize:0,
                        enforce:true
                    },
                    antDesignVue: {
                        name:"chunk-antDesignVue",
                        test: /[\/]node_modules[\/]_?ant-design-vue(.*)/,
                        priority: 39,
                        minSize:0,
                        enforce:true
                    },
                    antDesign: {
                        name:"chunk-antDesign",
                        test: /[\/]node_modules[\/]_?@ant-design(.*)/,
                        priority: 39,
                        minSize:0,
                        enforce:true
                    },
                    moment: {
                        name: "chunk-moment",
                        priority: 39,
                        test: /[\/]node_modules[\/]_?moment(.*)/,
                        minSize:0,
                        enforce:true
                    },
                    jeecg: {
                        name:"chunk-jeecg",
                        test: /[\/]node_modules[\/]_?@jeecg(.*)/,
                        priority: 39,
                        minSize:0,
                        enforce:true
                    },
                    quill: {
                        name:"chunk-quill",
                        test: /[\/]node_modules[\/]_?quill(.*)/,
                        priority: 39,
                        minSize:0,
                        chunks: "async",//仅打包异步引用的依赖
                    },
                    nodesAsync: {
                        name:"chunk-nodesAsync",
                        test: /[\/]node_modules[\/]/,
                        priority: 10,
                        minChunks: 2,
                        chunks: "async",//仅打包异步引用的依赖
                        reuseExistingChunk:true
                    },
                    nodesInitial: {
                        name:"chunk-nodesInitial",
                        test: /[\/]node_modules[\/]/,
                        priority: 9,
                        minChunks: 1,
                        chunks: "initial",//仅打包同步引用的依赖
                        reuseExistingChunk:true
                    },
                    components: {
                        name: "chunk-components",
                        test: resolve('src/components'),
                        minChunks: 2,
                        priority: 5,
                        reuseExistingChunk: true
                    }
                }
            })
            config.optimization.runtimeChunk("single")
            config.plugin('webpack-bundle-analyzer').use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
        })
    }
}

打包后的结果图如下:在这里插入图片描述
可以明显看到这次的分块里nodesInitial已经不见了,因为已经被分成其他的小块,但为什么这次的包总大小比上次的大300K左右呢,其实是因为抽离出来之后有部分依赖在其他模块还有引用,无法完全分割只能重复打包,这个可以根据打包情况调整代码的引用方式;

分包过程中可能发现大文件会转移,比如一开始可能是chunk-nodesInitial.js很大,增加缓存组来分割以后,发现nodesInitial文件不见了,这时候反而app.xxxxx.js变得很大,这个问题我还没想明白,只看哪个文件大,对它分包就行;

缓存组分包过程中如果发现分不出来,可以加上enforce: true参数,有点强行拽出来的意思,小编在分块过程中也发现这个情况,比如增加缓存组antDesignVue后,打包是可以独立分离出来的,但是如果再加一个缓存组moment或jeecg,会发现,可能jeecg分出来了,但是ant-design-vue又被拽回去原来的包了,要不就是jeecg分不出来,这时候只要加了enforce: true参数,就能分出来,试过不是权重优先级priority的问题,还要多研究研究,过程中基本是看结果图,看哪个文件大,就分一个缓存组;

本次案例分块的主要逻辑是,先分同步和异步两大块,再从这两大块里找最大的继续分割,同步分出来的就都使用chunks:”initial”,异步分出来的就都是chunks:”async”,一直往下分……

那分到什么程度为止呢,小编的结论是,如果继续分下去,总包变大,那基本就可以停止了,因为总包变大意味着有依赖重复打包了,就不要尝试抽离这个包,换一个包继续尝试,如果大文件里每一个包都分不出来,那就不继续分了,到这个程度基本上分包已到了相对合理的地步,但如果分到总包变大还有大文件,可以尝试换个角度分块,一开始分的两个大包大小最好接近二等分,说明两个包之间的代码耦合度比较低,越低越利于分割,多试试;

本次的分块成果:由原来的192个文件,减少到182个文件,最大文件值由6M缩小到所有文件不超过2M,因为有些单个文件本来就是1M多的,比如formMaking,已经没法再分了,所以暂时优化至此,后续深入了解再做调整;

还有个要提醒的问题,就是使用enforce: true参数后,不知会不会引发使用该组件的页面空白,大家可以自己验证下;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

chainWebpack之optimization.splitChunks的cacheGroups缓存组代码分块实践案 的相关文章

  • Jquery - 如何替换div中的一个单词?

    我有一个从其他位置复制的 div 我需要更改其中的一个单词 这是 HTML div class dealer addy 8726 N Royal Ln br Irving TX 75063 br email protected cdn cg
  • 如何使用角度在垫选择嵌套值中包含过滤器

    我正在使用带有嵌套下拉菜单的有角材料 下拉值以父级和子级为基础嵌套 我面临两个问题 自动建议不起作用 如果我输入父名称或其关联的子名称 则必须以展开模式过滤并显示特定的父视图 如果我展开第一个父视图并尝试展开第二个父视图 则第一个父视图应在
  • 使用 Passport 进行 Node.js 身份验证:如果字段丢失,如何闪烁消息?

    我正在使用 Passport js 如果我的表单字段为空 我想显示一条消息 但我不知道该怎么做 因为如果缺少护照 则不会触发策略回调 我真的希望这个用例更加清晰 而且我不想修改护照 我感觉有办法 但不知道在哪里 我尝试使用路线的回调 app
  • 工厂函数方法不更新变量

    我正在尝试设置一个工厂函数 该函数将更新所述工厂函数中特定变量的值 这看起来实现起来很简单 但是 每当我测试它时 我都会得到变量的原始设置值 而不是更新后的值 我确信在范围界定方面我缺少一些细微差别 但这里到底发生了什么 const fac
  • 范围滑块 javascript 以小时和分钟为单位

    我试图分别以小时和分钟为单位显示滑块的值 我对 JS 还很陌生 并且仍在尝试了解它是如何工作的 到目前为止 我所拥有的是一个工作滑块 其最大值为 1440 24 小时内的分钟 在 JS 中 我尝试将其划分为小时和分钟 并使用 innerHt
  • 错误:捆绑失败:TypeError:无法读取未定义的属性“transformFile”,React Native

    每当我运行react native start时 我都会收到此错误 但是最近在我在节点模块中安装了firebase tools和stripe之后 这种情况开始发生 但在此之前它会运行得很好 这也是错误的堆栈跟踪 ffff 127 0 0 1
  • JavaScript:所有标准内置对象实际上都是构造函数吗?

    我最近一直在研究 JavaScript 在花了几个月的时间之后 我仍然对一些内部结构感到困惑 具体来说 我试图理解所谓的标准内置对象 https developer mozilla org en US docs Web JavaScript
  • JavaScript 回调的效率

    我只是想证实我的一个怀疑 我偶然发现了一篇文章 建议按以下方式使用 Socket io var app require express createServer var io require socket io listen app app
  • 按周将对象数组拆分为组

    我有一个包含这样的数据的对象数组 date 01 01 2017 00 00 00 dataField1 dataField2 date 01 02 2017 00 00 00 dataField1 dataField2 date 01 1
  • pointdown 与 onclick:有什么区别?

    两者有什么区别onpointerdown and onclick事件处理程序 有任何实际差异吗 事件在 DOM 树上传播的方式不一样吗 是否有一些设备仅响应这些事件之一 我最初以为这只是pointerdown在触摸设备或笔中触发 但是onc
  • 如何在 Electron 中使窗口大小响应。 (打开应用程序时)

    我最近开始在 Electron 上制作一个应用程序 我想让窗口具有响应能力 例如 如果我在不同的屏幕上打开应用程序 它应该根据屏幕尺寸以全尺寸打开 我的代码 app on ready gt const htmlPath path join
  • 未处理的 Promise 拒绝:push.on 不是函数

    我正在使用离子2 我得到这个打字稿error当尝试设置推送通知时 我从教程中复制了此示例代码 因此预计它可以工作 我一定有什么问题 任何想法请 Unhandled Promise rejection push on is not a fun
  • Firebase 停止监听 onAuthStateChanged

    从版本 3 0 0 开始 我很难删除身份验证状态更改侦听器 要根据文档启动侦听器 firebase auth onAuthStateChanged function user handle it 但是 我在文档中找不到任何涉及删除身份验证状
  • 纯 JavaScript 工具提示

    我正在尝试用纯 JavaScript 制作一个工具提示 显示在hover 就像 Stack Overflow 中将鼠标悬停在个人资料名称 a 上一样div显示 我尝试使用onmouseover onmouseout并添加了setTimeou
  • Netlify 正在显示我的 Gatsby 网站的 html 版本

    网站建设位于https 5efbc255ca51be00080b5219 epic raman 086510 netlify app https 5efbc255ca51be00080b5219 epic raman 086510 netl
  • Python 中 Javascript 的 reduce()、map() 和 filter() 的等价物是什么?

    Python 的等价物是什么 Javascript function wordParts currentPart lastPart return currentPart lastPart word Che mis try console l
  • 如何在从数据库异步加载中用占位符替换不存在的图像

    我有一个包含图像的数据库 我需要根据用户的请求即时加载这些图像 这些图像将作为包含 div 容器中的单独 s 的背景图像 类似于图像滚动条 该数据库当前是本地数据库 但这不是我的问题 问题是数据库可能没有我请求的所有图像 并且我可能会得到一
  • 未捕获的类型错误:无法分配给只读属性

    我正在尝试 Nicholas Zakas 所著的 Professional JavaScript for Web Developers 一书中的这个非常简单的示例 但我不知道我在这里做错了什么 我错过了一些非常简单的事情 但我被困住了 这是
  • jQuery:array[i].children() 不是函数

    以下代码的灵感来自http ignorethecode net blog 2010 04 20 footnotes http ignorethecode net blog 2010 04 20 footnotes 当您将光标移到脚注符号上时
  • 离子选择:预选值在单击一次之前是不可见的

    我刚刚在 ionic 版本 6 中创建了一个 ion select 我的问题是 我在页面加载时已成功预选了一个值 但此预选值未显示在 UI 中 它只是在我单击选择之后出现 但在它没有出现之前 如图 2 所示 我在 ionViewWillEn

随机推荐

  • RHEL8、CentOS8配置本地YUM源

    1 挂载光盘镜像到一个指定的目录 2 创建一个仓库配置文件 3 编辑 etc fstab配置文件 让光盘开机自动挂载 4 使用yum dnf命令来安装软件 仓库名称 具有唯一性的标识名称 不应与其他软件仓库发生冲突 描述信息 name 可以
  • 如何将JSON字符串转化成对象

    在这里只能使用ObjiectMapper这个类才能将Json字符串转成对象的格式进行输出 话不多说 直接上代码 实体类 实体类 Setter Getter public class UserInfo implements Serializa
  • 搭建个人备忘录中心服务memos、轻量级笔记服务

    目录 一 源码 二 官网 三 搭建 四 使用 一 源码 GitHub usememos memos A privacy first lightweight note taking service Easily capture and sha
  • 计算机网络 第四章网络层(6)网络地址转换 NAT多协议标记交换 MPLS MPLS 协议的基本原理

    关注公众号凡花花的小窝 收获更多的考研计算机专业编程相关的资料 4 8 2 网络地址转换 NAT 问题 在专用网上使用专用地址的主机如何与互联网上的主机通信 并不需要加密 解决 再申请一些全球 IP 地址 但这在很多情况下是不容易做到的 采
  • 机器学习 :训练集、验证集、测试集分配比例

    根据 统计学习方法 中的观点 如果给定的样本数据充足 进行模型选择的一种简单方法是随机地将数据集切分成三部分 分别为训练集 training set 验证集 validation set 和测试集 test set 训练集用来训练模型 验证
  • JS解析详细分析

    1 确定js的位置 1 1 观察按钮的绑定js事件 通过点击按钮 然后点击Event Listener 部分网站可以找到绑定的事件 对应的 只需要点击即可跳转到js的位置 1 2 通过search all file 来搜索 部分网站的按钮可
  • 如何检查 Linux 中的程序和监听的端口

    在 Linux 或者类 Unix 中 我该如何检查某个端口是否被占用 我又该如何验证 Linux 服务器中有哪些端口处于监听状态 验证哪些端口在服务器的网络接口上处于监听状态是非常重要的 你需要注意那些开放端口来检测网络入侵 除了网络入侵
  • LRU Cache的数据结构选择以及实现

    LRU LRU是Least Recently Used的缩写 意思是最近最少使用 它是一种Cache替换算法 什么是Cache 狭义的Cache指的是位于CPU和主存间的快速RAM 通常它不像系统主存那样使用DRAM技术 而使用昂贵但较快速
  • STC 32位8051单片机开发实例教程 二 I/O工作模式及其配置

    1 I O工作模式 STC 32G系列单片机最多有64Pin引脚 最多有60个I O口 如下图示 STC32G系列单片机的 I O口都有4种工作模式 准双向口 弱上拉 推挽输出 强上拉 高阻输入 电流不能流入也不能流出 开漏输出 P30 P
  • C/C++语言中的注释:功能、符号和使用方法详解

    目录 引言 注释的功能 注释符号 单行注释 多行注释 注释结尾问题 利用预处理实现多行注释 示例代码和解析 结论 引言 在C语言中 注释是一种非常有用的工具 可以帮助程序员在代码中添加说明 解释和备注 本文将深入探讨注释的功能 不同注释符号
  • MAC中文版 FCPX V10.6.5 专属视频剪辑后期工具及其插件安装使用教程

    Final Cut Pro X简介 Final Cut Pro X又名FCPX 是MAC上非常不错的视频非线性剪辑软件 它剪辑速度超凡 具有先进的调色功能 HDR 视频支持 以及 ProRes RAW 让剪辑 音轨 图形特效 整片输出 支持
  • 网络 ip tcp/udp dhcp dns rip/ospf

    网络 七层网络模型 物理层 数据链路层 网络层 传输层 会话层 表示层 应用层 物理层定义了一系列传输介质的电气标准 这个是弱电工程师关心的 数据链路层 封装成帧 差别检错 透明传输 MAC地址 通过CRC循环冗余校验生成校验码 放在数据包
  • 44黑马QT笔记之IP地址的划分与是否在同一网段

    44黑马QT笔记之IP地址的划分与是否在同一网段 前提 1 网络ID ip地址的第一个字节 2 网络地址 在这里你可以认为它就是网络ID 3 网段 用来区分网络上的主机是否在同一区段内 只要知道ip地址和子网掩码就知道该网段 在局域网中只有
  • MySQL多字段去重

    创建学生成绩表grade grade表的字段说明 id表示学生编号 name表示学生姓名 gender表示学生性别 score表示学生分数 create table grade id int name char 1 gender char
  • 自动化测试学习路线

    1 前端开发基础 HTML JS CSS 2 浏览器调试工具 F12 FireBug Chrome浏览器 3 接口测试工具使用 PostMan SoapUI Jmeter HttpClient UrlConnection Requests
  • ubuntu下编译linux内核

    1 下载linux内核源文件 www kernel org 2 安装有关编译工具 sudo apt get install build essential kernel package libncurses5 dev 3 把内核复制到 us
  • 【老生谈算法】基于matlab的车牌识别算法详解及程序源码——车牌识别算法

    基于matlab的车牌识别系统设计与算法原理 大家好 今天给大家介绍基于matlab的车牌识别系统设计与原理 车牌识别系统 License Plate Recognition 简称LPR 是智能交通系统 ITS 的核心组成部分 在现代交通管
  • 组件不更新怎么办!??

    适合多日 碰到了个莫名其妙的问题 上传图片后 列表组件没有更新 非要刷新页面或者切换组件才能更新 之前的暂时解决方案是 上传图片后手动刷新页面 非常不友好的交互 终于忍不住了 想办法解决它 想了很多办法 怎么都没有办法刷新页面 最后突然想到
  • python不同数据类型进行转换

    代码实现 代码如下 示例 name 张三 age 20 print 我叫 name 今年 str age 岁 不同类型转换为str类型 a 10 b 198 8 c False print str a str b str c 转为字符串格式
  • chainWebpack之optimization.splitChunks的cacheGroups缓存组代码分块实践案

    研究了好几天webpack打包 我们项目是vue的高版本 已经没有了webpack config js文件了 是直接在vue config js里的chainWebpack方法直接配置 这样做法的好处是用户既可以保留webpack的默认配置