前言
这两篇指南(网络层、浏览器层)致力于采用清晰易懂的形式带大家熟悉常见的前端性能优化方案。文章不再是简单的罗列的方案点(平铺的目录结构),而是采用分类总结的方式(清晰的目录结构)。
希望能更加方便大家学习和查阅!
网络层
加快 HTTP 请求
- 合理采用 CDN,将前端的静态资源放到就近的 CDN 上,能加快请求的速度
- 必要时,可以开启 HTTP 长链接,减少每次 HTTP 请求时顺带的 TCP 三次握手引起的开销
减少请求次数
两种方式
要减少请求的次数,主要是依靠这两点:
- 对于接口 而言,我们可以直接采用 WebSocket 或 SSE 这些长链接的方式来替换/减少轮询代码和重复的请求
- 对于静态资源 而言,我们可以在打包构建前端时合并静态资源
WebSocket 和 SSE 这两个东西我这里就不展开讲了,都是前后端通信的方式,比较简单,大家可以自己上网翻阅一下关于他们的文档。
下面主要讲一讲如何合并静态资源:
合并静态资源
chunk 分块
用 Vite 来举例的话,我们可以对 build.rollupOptions
选项按如下规律配置:
ts
rollupOptions: {
output: {
/**
* 分块策略
* 1. 注意这些包名必须存在,否则打包会报错
* 2. 如果你不想自定义 chunk 分割策略,可以直接移除这段配置
*/
manualChunks: {
vue: ["vue", "vue-router", "pinia"],
element: ["element-plus", "@element-plus/icons-vue"],
vxe: ["vxe-table", "vxe-table-plugin-element", "xe-utils"]
}
}
}
简单解释解释就是:在配置前,vue
、vue-router
、pinia
分别会产生三个 chunk 文件。配置后合并为了一个 chunk 文件。
这种优化方式,非常适合于体积小的 chunk 文件,我们将其合并成一个后,能有效减少请求次数。但是要保证合并后的文件不能太大,不然单文件太大,也是一种非常耗时的开销。
雪碧图
雪碧图可以看做是将多个图片合并为一张大图,然后通过代码来控制显示大图中的某一张子图,所以我也将其放到了合并静态资源目录下。
无论是以前的 CSS Sprites 还是现在常用的 SVG Symbol,都能有效减少前端去请求图片资源的次数,如果页面上图片太多,我们可以往这个优化方向考虑。
既然提到了图片,那就多说两句:
图片的优化又是一个很大的方向,里面不仅涉及到雪碧图 ,还涉及图片格式 、图片压缩 、图片懒加载等知识点。等有机会再写文章详细来向大家介绍。
缓存
浏览器的缓存有许多种,我们通常最喜欢讨论和运用 HTTP 缓存 ,这种缓存也是面试中最常考的一种,它分为强缓存 和协商缓存两种类型。
强缓存
当前端向服务器发送 HTTP 请求时,如果强缓存生效了,那么将直接从缓存中获取资源,无须再和服务端进行通信。
强缓存生效的情况下,HTTP Code 依旧会呈现 200
,不过会附带一些缓存说明,比如 form disk cache
等类似于 form xxx cache
的说明。
强缓存的优先级比协商缓存高一些,也就说一个 HTTP 请求会优先尝试强缓存能否可行。对能否可行的判断,是需要 HTTP Header 中
Cache-Control
和Expires
两个字段来配合的。
协商缓存
协商缓存的话,还是会向服务器进行通信,询问服务器该 HTTP 请求对应的资源是需要重新拉取还是直接去缓存里拿。
如果对应的资源并没有任何改动,服务器就会通知客户端去缓存里拿(重定向到客户端缓存)
当协商缓存生效的时候,HTTP Code 会呈现出 304
。
本地存储
合理的运用本地存储,将一些资源保存下来,也会减少后续发送 HTTP 请求的一种方式!
Cookie
Cookie 的话,不建议拿来存储过多的东西。一个是因为它容量不够大,只有 4KB 左右。二个是因为它可能会跟随着每次 HTTP 请求传来传去,多多少少占用一点体积,导致不必要的开销。
拿来存储一些鉴权类小信息是不错的选择,比如类 Token。
Session Storage
Session Storage 是会话级别的本地存储方案,也就是说,关闭会话窗口后,数据就会丢失了。
用来存储一些一次性的信息是不错的选择,比如用户信息。
Local Storage
Local Storage 则是持久化的本地存储方案,只要不手动删除,存储的数据就会一直存在。
想必大家对它并不陌生,常用来存储一些每次打开网页都会用到的且不经常改动的信息,比如记住账号、Base64 图片、网站配置
减少请求体积
我们可以在打包构建前端时尽可能的压缩静态资源
代码压缩混淆
比如我们可以在 Vite 中将 build.minify
选项配置为 esbuild
,开启 Esbuild 作为混淆器。再搭配下面的配置来移除生产环境的 console.log
、debugger
、注释
:
ts
/** 混淆器 */
esbuild: {
/** 打包时移除 console.log */
pure: ["console.log"],
/** 打包时移除 debugger */
drop: ["debugger"],
/** 打包时移除所有注释 */
legalComments: "none"
}
Gzip 压缩
为了减少 HTTP 请求的体积,我们还最常用的方式就是开启 HTTP 压缩 ,而其中的 Gzip 压缩,就是目前最最常用的一种方式。
开启 Gzip 压缩后呢,前端在向服务器请求资源时,服务器会将压缩后的资源响应给客户端,客户端这边拿到被压缩后的资源后会自动进行解压操作,释放出原始资源后进行再进行正常渲染等操作。
由于资源被压缩,所以传输过程的时间成本会大大减少。
但这里的话,我们需要注意的一件事情就是:压缩和解压过程产生的开销会不会太大,会不会抹平了传输过程减少的时间成本?
答案肯定是不会的,大家可以放心的开启 Gzip 压缩。
那又有人问,如何开启 Gzip 压缩呢?
有两种方式:
- 请求运维同学在服务器端开启 Gzip 压缩
这种方式呢,和前端没什么关系,前端正常 Build 进行打包构建即可。
- 前端在 Build 时,生产 Gzip 压缩后的产物
这种方式的话,需要前端根据不同的构建工具进行相应的配置,Webpack 和 Vite 等常见的工具都是支持的。
Build 后,除了正常的产物外,还有一份 Gzip 压缩后的产物。需要将这两份产物同时部署到服务器上。
选择第二种方式有一个优点,那就是可以省去第一种方式中,服务器在响应前需要先执行压缩程序的时间成本。
Tree Shaking
这功能叫 Tree Shaking
,也就是将枯萎的树叶摇晃下来(开玩笑),对我们来说,大致意思就是将没用到的代码删除掉。
比如 Vite 的底层 Rollup 就提供了 Tree Shaking 的能力。得益于这种能力,我们在使用自己的组件或者三方库时,如果不是用到了它们的全部,那么打包时将自动删除掉没用到的部分代码,大大减少了打包构建完成后的代码体积。
不过有一点需要记住的是,Tree Shaking 只支持 ES6 的模块化方式。所以建议大家在写代码和选择第三方库时,尽可能使用 ES6 的导入导出方式和选择 ES 版本的库!
按需导入
很多三方库都提供了按需导入的模式,比如 Element Plus 组件库。这时候我们就可以采用这种方式而非完整导入,这样也可以大大减少打包构建后对应的静态资源体积。
懒加载
我们开发 Vue/React 等应用时,应该直接采用路由懒加载的模式。Vue Router 官方解释的非常清楚,我这里就直接引用了:
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。
这一点也正好对应着我们经常遇见的面试题:前端首屏优化
浏览器层
正在努力编写中...
链接:⚡ 前端性能优化指南(浏览器层篇)
最后
如果对你有帮助,希望点赞收藏支持一下!