前端项目优化+webpack splitChunks分割代码项目优化实战

前言:最近重构一个项目(基于umi2脚手架搭建的),打包上线后发现包非常大,决定将项目优化一下,打包后的dist文件

image

可以看到打包后的dist文件有16M,然后部署上去发现首次打开蜗牛🐌般的速度,原因有一个公共依赖文件有7.6M之大,我giao,这怎么行呢!?

image

如果浏览器选项勾选了不允许缓存,那么将导致每次打开页面或者刷新都将会几乎如同首次加载一样,加载这些文件,每次都这么慢,致命致命致致命。

这在项目部署上线,用户访问时候是非常致命的,下面我们开始针对这个进行优化:

一、压缩静态资源

先从静态资源入手:图片。看下截图文件ued给的大小居然一张图片2M多,这...,索性自己来

image

使用这个网址: **https://tinypng.com/ **这个网站可以在保持清晰度变化不大的情况下把图片大小压缩,把稍微比较大的图片上传压缩, 可以看到,左侧是原图大小,右侧是压缩后的代码大小

image

把文件替换了,使用压缩后的图片。

二、使用打包分析工具---去除不需要的依赖

webpack的打包分析工具,我项目用的是umi2脚手架,所以直接根据文档配置即可,在.env文件新增:ANALYZE=1,然后umi build打包,会自动打开打包后的分析图。

image

Stat size :代表原始文件大小(文件没有经过任何处理的)

parsed size :解析后的文件大小,输出压缩过后的文件大小。(右键查看文件属性大小和这个大小一致)

gzipped:经过gizp压缩过后的代码大小(例如nginx可以开启gzip压缩),这里实际也是最终打开页面下载的文件大小是这个大。

把图上的包模块标注分析一下,红色的是目前以知的插件依赖,其他是引用的插件中有需要用到的插件:

image

首先分析下:echart、G6关系图谱、超图(地图)、代码编辑器这几个都是我项目中首页加载不到的(依据你的项目情况而定),我们需要把他从vendors.async.js文件中抽出来。这样就可以在打开首页时候(第一次加载),避免引入不需要的插件代码。

1、先从moment入手,时间插件上有很多国际语言,我们不需要用到,需要将它过滤掉:

image

在.umirc.js文件加入webpack,umi里面使用的是 chainWebpack

image

具体写法如下:

[
复制代码

](javascript:void(0); "复制代码")

<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;"> chainWebpack(config) { //过滤掉momnet的那些不使用的国际化文件
config.plugin('replace').use(require('webpack').ContextReplacementPlugin).tap(() => { return [/moment[/\]locale$/, /zh-cn/];
});
},</pre>

[
复制代码

](javascript:void(0); "复制代码")

接下来,umi build打包测试看效果moment时间依赖包体积明显了500k左右:

image

2、处理下echart,写法换成按需引入,打包时候也会按需打包

再次打包查看,dist文件大小,明显小了非常多(可能图片主要静态文件大小)!

image

我们发现即使这么做了,虽然把项目总体减小了,但是加载时候还有需要加载一个超级大的主文件,这就是一个问题, 之后我们应该用包分析模块显示的大小来做参考调整分割。进行第三步现在~~~

三、使用webpack分割代码---把主文件的依赖分离出来

以上步骤完成以后开始优化项目加载的具体操作,这里解释下为什么要这么做,因为我们通过分析发现没分割之前,浏览器打开页面时候下载的那个vendor.async.js文件非常大,并且里面的依赖不是我们每次都要用到,所以我们需要把这个文件拆分。

整理思路:拆分成什么样?我们需要把不是每次用到的依赖拆分出来,让他使用到的时候再根据那个页面按需要加载。

需要根据实际情况来定夺,实际目的只有一个,把主文件(里面的公共依赖文件用得比较少的)分割出来,多出引用的放在主文件里面(避免每次都要加载那部分的依赖代码文件),例如我项目中:

关系图谱只有一个页面有用到、echart只有一个页面用到、地图只有两个页面用到(一个前台一个后台),考虑到主文件太大因素,需要将这几个依赖拆分出来,等打开那几个特定页面时候按需要去加载这依赖代码。

开始拆分:使用webpck的 splitChunks

以下是webpack分割代码字段的解释说明:

[
复制代码

](javascript:void(0); "复制代码")

<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;"> optimization: {
minimize: true,
splitChunks: {
chunks: 'async', //all: 不管文件是动态还是非动态载入,统一将文件分离。当页面首次载入会引入所有的包
//async: 将异步加载的文件分离,首次一般不引入,到需要异步引入的组件才会引入。
//initial:将异步和非异步的文件分离,如果一个文件被异步引入也被非异步引入,那它会被打包两次(注意和all区别),用于分离页面首次需要加载的包。
minSize: 30000,// 文件最小打包体积,单位byte,默认30000
minChunks: 1, //最少引入的次数 2:引入两次及以上被打包
automaticNameDelimiter: '.',// 打包分割符
cacheGroups: {
vendors: { // 项目基本框架等
chunks: 'all',
test: /(react|react-dom|react-dom-router|babel-polyfill|react-redux)/,
priority: 100,
name: 'vendors',
},
echartsVenodr: { // 异步加载echarts包
test: /echarts/,
priority: 100, // 高于async-commons优先级
name: 'echartsVenodr',
chunks: 'all',
minChunks: 5, //最少引入的次数 2:引入两次及以上被打包
}, },
},
},</pre>

[
复制代码

](javascript:void(0); "复制代码")

<pre style="margin: 0px; padding: 0px; overflow: auto; white-space: pre-wrap; color: rgb(51, 51, 51); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">chunks:有三个值:分别代表你想优化打包优化的类型,默认为async,截图看下这个意思
async:动态引入


image

</pre>

<pre style="margin: 0px; padding: 0px; overflow: auto; white-space: pre-wrap; color: rgb(51, 51, 51); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">initial:直接引入加载</pre>

image

all:以上两者都进行打包分割优化

<pre style="margin: 0px; padding: 0px; overflow: auto; white-space: pre-wrap; color: rgb(51, 51, 51); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">最终代码:</pre>

[
复制代码

](javascript:void(0); "复制代码")

<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;"> chainWebpack(config) { //过滤掉momnet的那些不使用的国际化文件
config.plugin('replace').use(require('webpack').ContextReplacementPlugin).tap(() => { return [/moment[/\]locale$/, /zh-cn/];
});
config.merge({
optimization: {
minimize: true,
splitChunks: {
chunks: 'all', //all: 不管文件是动态还是非动态载入,统一将文件分离。当页面首次载入会引入所有的包
//async: 将异步加载的文件分离,首次一般不引入,到需要异步引入的组件才会引入。
//initial:将异步和非异步的文件分离,如果一个文件被异步引入也被非异步引入,那它会被打包两次(注意和all区别),用于分离页面首次需要加载的包。
minSize: 30000,// 文件最小打包体积,单位byte,默认30000
minChunks: 1, //最少引入的次数 2:引入两次及以上被打包
automaticNameDelimiter: '.',// 打包分割符
cacheGroups: { //优先级高于外面配置,理解为先分割后合并
vendors: { // 项目基本框架等
chunks: 'all',
test: /(react|react-dom|react-dom-router|babel-polyfill|react-redux)/,
priority: 100,
name: 'vendors',
},
echartsVenodr: { // 异步加载echarts包
test: /echarts/,
priority: 100, // 高于async-commons优先级
name: 'echartsVenodr',
chunks: 'async', // minChunks: 5, //最少引入的次数 2:引入两次及以上被打包
},
antvVenodr: { // 异步加载@antv包
test: /@antv/,
priority: 100, // 高于async-commons优先级
name: 'antvVenodr',
chunks: 'async',
}, 'async-commons': { // 异步加载公共包、组件等
chunks: 'async',
minChunks: 2,
name: 'async-commons',
priority: 90,
},
commons: { // 其他同步加载公共包
chunks: 'all',
minChunks: 2,
name: 'commons',
priority: 80,
},
},
},
},
});
},</pre>

[
复制代码

](javascript:void(0); "复制代码")

实际效果:可以看到echart包、g6、codemirror这几个插件被单独抽离出来成js文件,不会再没用到情况下加载引入。

image

补充:在较早之前的版本,我们知道webpack可以将js文件打包压缩成js.gz文件,使得文件体积大大减小,但是现在大部分前后端分离的场景都是将前端项目部署在nginx,nginx上有个功能能将js压缩成gizp格式传输给浏览器,这也是为什么现在大都打包成js格式就可以,因为nginx上只要开启压缩功能,他会检测是否有.gz文件,没有的话会自动将js文件压缩传输。

总结:

1、首先通过webpack手段优化项目,(开启按需加载前提),只能对打包的代码分割,总体积上基本不可有很大的改变(某些情况可能会导致dist总体积变大,因为分离公共依赖包,能把公共的东西从vendors.async.js文件提取出来,同时也会把这个依赖拿出来在放到另一个或者另两个js文件里面做到按需加载,好处是能使得某个页面时候不需要一次引入这么大的一个文件。)

2、通过修改代码优化:例如将插件通过Index.html内的script标签方式引入(外网环境可以直接用CDN方式),这样可以使得公共依赖包减少体积。

3、简单粗暴图片压缩(webpack也有其他的图片转换压缩,例如将小于多少kb的图片转换为base64格式)

4、性能优化:多写PureComponent组件而不是普通的Component组件,因为pure有自动判断shouldComponentUpdate这个组件生命周期的功能(也只能进行浅比较:数组、对象类型,因为存储堆栈,引用地址指向问题还是会频繁更新)。备注:如果有写hook组件,那么使用hook的memo,usememo,usecallback,是最佳方案!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容