概要内容
- 项目背景
- 性能优化结果
- 打包后资源体积
- 首屏页面加载速度
- 优化步骤:
- 压缩:Image\CSS\JS\Fonts\Html
- Tree Shaking
- splitChunks
- dns-fetch
- cdn加速
经过一周多的努力,我把项目打包后的资源减小了131%,首页加载速度提升了584%,下面直接给出对比数据方便查看,随后我会把我的优化方法分享给大家,希望对后门的同学提供一些帮助。
项目背景:
App 是由unity + 内嵌h5 的方式实现的,App 支持两种内核IE 和 Chrome。为了方便数据统计,Chrome 内核模式我直接利用Chrome测试的,直接拿的Chrome Network 面板的Finish 与 DOMContentLoaded 的数据。IE内核模式对于H5项目的早期版本,IE 会时不时的崩溃,所以只能通过代码的方式进行统计。
性能优化结果
打包后资源体积



首屏页面加载速度


图片处理
由于webpack对图片处理的压缩比不高,所以就利用陪伴我工作很多年的图片压缩工具**tinypng**来进行压缩
CSS处理
抽离CSS文件,filename 命名格式采用contenthash 利用cdn
chunkhash存在一个问题,就是js关联的css,只要js变化,打包后的cs的hash值也会变化,所以我们改成contenthash

压缩CSS:css-minimizer-webpack-plugin
optimization: {
minimizer: [new CssMinimizerPlugin()],
}
JS处理
Tree Shaking
-
测试代码
//src\utils\math.js export function square(x) { return x * x; } export function cube(x) { return x * x * x; }//src\main.js import { cube } from "@/utils/math"; console.log("cube:", cube(5)); -
修改配置
- mode 设置成development (原因:为了方便查看打包后的结果)
- 添加usedExports

- 打包后结果:未被使用的代码,已标记

- 通过设置mode为production,默认已支持了tree shaking,所以不用再配置了

查看打包后的结果

压缩:terser-webpack-plugin
optimization: {
minimizer: [new TerserPlugin(), new CssMinimizerPlugin()],
},
Fonts
- 删除废弃的资源
- 去掉重复的图标
Html
只需要将mode 改成production即可,webpack5 内置插件对html进行了压缩,所以不需要做什么
分析打包后的资源
在进行cdn加速之前,我们需要安装webpack-bundle-analyzer,来直观的分享我们打出来的包到底什么在占用我们的资源体积
{
plugins:
[
...
new BundleAnalyzerPlugin()
]
}
再次打包后自动启动一个本地服务,内容如下:

根据此图:我们就可以快速定位到哪些模块在占用体积,所以我们把大的模块缩小(比如:删除冗余或废弃、按需引入、寻找更小的库替代等)。

把element-ui\vue\vue-router\vuex等改成cdn形式,再次打包看bundle analyzer,发现这些模块都没有显示了。(提示:这个图我们还可以继续优化)
接下来把抽离的库,打包过程中自动注入到模板html文件中,为此我单独写了一个插件 html-dynamic-injection (提示:后面我会将此插件写成单独的一个库,然后发布至npm)
CDN
根据html-dynamic-injection添加配置,自动注入cdn配置、style配置、dns-fetch配置,格式如下
-
参数规则
{ scripts: [ { importName: "", globalVariableName: "", position: "head" | "body", src: "", integrity: null | "", async: false | true, defer: null | "defer", rel: null | "preload" | "prefetch", importance: "high" | "low" | "auto", crossorigin: null | "anonymous", }, ], links: [ { importName: null | "", href: "", ref: null | "stylesheet" | "dns-prefetch", crossorigin: null | "anonymous", }, ], } -
添加配置(提示:为了展示以下只是部分配置)
function getExternalLibs(isMin) { let extension = isMin ? ".min.js" : ".js"; let cssExtension = isMin ? ".min.css" : ".css"; return { scripts: [ ... { importName: "element-ui", globalVariableName: "Element", position: "body", src: `https://unpkg.com/element-ui@2.15.6/lib/index.js`, defer: "defer", async: true, importance: "low", crossorigin: null, }, ], links: [ { importName: "element-ui/lib/theme-chalk/index.css", ref: "stylesheet", href: `https://unpkg.com/element-ui@2.15.6/lib/theme-chalk/index.css`, crossorigin: null, }, { rel: "dns-prefetch", href: "https://unpkg.com", crossorigin: null, }, ], }; } -
引入插件 + 使用
const HtmlDynamicInjectionPlugin = require("../plugins/html-dynamic-injection"); ... plugins: [ ... new HtmlDynamicInjectionPlugin(getExternalLibs()), ] -
打包后结果
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>title</title> <link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.6/lib/theme-chalk/index.css"> <link rel="dns-prefetch" href="https://unpkg.com"> <link rel="dns-prefetch" href="https://cdn.jsdelivr.net"> <script crossorigin="anonymous" position="head" src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script> <script crossorigin="anonymous" position="head" src="https://cdn.jsdelivr.net/npm/vue-router@3.0.1/dist/vue-router.min.js"></script> <script crossorigin="anonymous" position="head" src="https://cdn.jsdelivr.net/npm/vuex@3.0.1/dist/vuex.min.js"></script> <script defer="defer" src="/static/js/main.ecae5beeb5463b7c5f8a.js"></script> <link href="/static/css/main.bfcb8559ae29d81822a8.css" rel="stylesheet"> </head> <body> <div id="app"></div> <script position="body" src="https://unpkg.com/element-ui@2.15.6/lib/index.js" defer="defer" async importance="low"></script> </body> </html>补充说明:注入资源到html里面是有讲究的
- link配置到head:快速解析dns解析域名
- style配置到head:提前把样式加载,避免页面展示出来样式错乱
- script 引入模块,引入位置、defer、importance、async、pre-fetch、pre-load 都会影响加载顺序和速度,推荐还是直接去MDN Web docs仔细去了解这些参数,来应对未来的网站性能优化
- 比如:vue 需要最先加载,所以我们将其放到head中
- 比如:element-ui,不是首页立马显示的,所以我们放到body中
具体demo:传送门
总结
- 不要止步不前,要跟随行业走:如果项目有必要,找到合适的时机,就及时更新项目环境(比如:webpack5 已经内置很多插件,代码分割、混淆 等等,都已经有默认配置了,无特殊要求可以不用配置)
- 如果对CDN 稳定性有要求,最好使用公司自己的,不要使用公共的CDN (原因:公共CDN 时快时慢,挺不稳定的)
遗留问题:
- element-ui : 自动注入css到html 与 手动在html模板中引入css,引入后的内容完全一样,但结果却是手动引入的可以成功加载,自动注入的却不加载,未找到具体原理,如果你知道原因,麻烦告诉我,感谢
至此感谢您的阅读,如果您有什么想法,欢迎给我留言~
以上:如发现有问题,欢迎留言指出,我及时更正
参考文献
- webpack.docschina.org/guides/tree…
- zh.wikipedia.org/wiki/副作用_(计…
- webpack.js.org/configurati…
- webpack.docschina.org/configurati…
- webpack.docschina.org/plugins/spl…
- babeljs.io/docs/en/bab…
- stackoverflow.com/questions/4…
- www.zuo11.com/blog/2020/1…
- element.eleme.cn/#/zh-CN/com…
- codepen.io/ziyoung/pen…
- tinypng.com/