近期对前端性能优化的总结
前端性能优化不管是在面试中还是在实际开发过程中,都是每一个前端开发工程师都必不可少的能力。本文总结本人多年开发经验中对前端性能优化的理解,希望对大家有所帮助,因涉及的优化方向较多,针对某些细节不再详细说明,大家有兴趣的可深入了解,话不多说,正文开始。
重要性
一个好的前端项目性能非常重要,特别是面向 ToC 的用户,好的用户体验可以极大的提高业务转化率,从而性能的好坏关乎到业务的营收。对于一个商业性公司只要是和钱相关,都是极其敏感且重要的。
度量方式
2020 年 Google 提出了新一代 Web 性能体验指标 Core Web Vitals,其中包括了 LCP、FID、CLS 三大指标。
Largest Contentful Paint (LCP): 衡量加载体验:为了提供良好的用户体验, LCP 应该在页面首次开始加载后的 2.5 秒内发生。
First Input Delay (FID): 衡量可交互性,为了提供良好的用户体验,页面的 FID 应当小于 100 毫秒。
Cumulative Layout Shift (CLS):衡量视觉稳定性,为了提供良好的用户体验,页面的 CLS 应保持小于 0.1。
针对这些指标直接可以通过浏览器开发工具中的 Lighthouse 得出是否达到标准。通过这样的方式得出的结论快速直观,对原本网站无侵入,不影响真实用户的性能。但是也有缺点,不支持复杂的业务逻辑场景,监测的数据量太小,不能还原大部分真实用户的使用情况。
所以为了得到真实且全面的数据,大部分的公司都会开发一套监测方案,或者使用第三方监测平台,这样会对网站有一定的性能影响,但是可通过更全面的性能数据分析可优化的方向。
性能优化方向
基于上面的三个体验指标,我们可以从页面加载的生命周期进行优化,页面加载前的预处理,加载过程中,页面渲染时,用户界面交互等几个阶段,下面将针对不同的阶段进行优化,大家可根据自己项目的情况针对性的选择优化。
加载前的预处理
使用 dns-prefetch、preconnect 减少 DNS 解析,建立 TCP 连接以及执行 TLS 握手时间,dns-prefetch: 告知浏览器对指定域名进行 DNS 解析。当后续请求该域名资源时可省掉 DNS 解析的时间。preconnect: 告知浏览器与指定域名的服务器建立连接。当后续请求该域名资源时,可直接使用已建立好的连接,省掉了 DNS+TCP+TLS 的时间
使用 preload/prefetch 让浏览器提前加载需要的资源,preload 可以指明哪些资源是在页面加载完成后即刻需要的,浏览器在主渲染机制介入前就进行预加载,这一机制使得资源可以更早的得到加载并可用,且更不易阻塞页面的初步渲染,进而提升性能;prefetch 其利用浏览器空闲时间来下载或预取用户在不久的将来可能访问的文档。切记不要将 preload 和 prefetch 进行混用,它们适用于不同的场景,如对同一个资源同时使用 preload 和 prefetch 会造成不必要的二次下载。
加载过程中
1. 尽可能的减小资源的大小
业务代码本身尽可能的不要重复,提高组件化的使用,提示代码的复用率,这里不止是 JS,CSS 样式也是一样
压缩静态资源,一般脚手架都默认会处理,自建项目可检查是否有压缩
html 中的 DOM 层级控制不要太深以及减少不必要的 DOM 使用,尽可能发挥伪元素及 CSS 的使用
检查项目的依赖包是否有重复引用的情况,不同的依赖包可能引用了同一个不同版本的包,可通过 webpack-bundle-analyzer 插件分析查看
UI 组件库或其他库使用 babel-plugin-import 插件进行按需加载
组件按需加载,使用 AsyncComponent 仅加载首屏组件
动态导入第三方比较大的模块,import('/modules/echart.js) .then((module) => {}),但不要滥用,结合实际场景使用
减小第三方库的大小,如 Moment.js/lodash 等,使用轻量级别替代方案或者自己重新实现
对首评秒开要求较高的,可对首屏请求的接口进行拆分,快速响应首屏需要用到的字段,其他的数据异步加载
使用 tree shaking,当我们在项目中引入其他模块时,他会自动将我们用不到的代码,或者永远不会执行的代码摇掉,在 Uglify 阶段查出,不打包到 bundle 中
HTTP 头部 Cookie 的精简,去除不必要的 Cookie,静态资源做独立域名部署,避免请求携带 Cookie
HTTP 头部开启 gzip 压缩,可大大减小网络传输的数据量
HTTP 头部开启 keep-alive
升级 HTTP 到 2.0,2.0 的头部压缩,减少了数据传输量,能够节省消息头占用的网络的流量,且还有多路复用等优势
2. 尽可能的减少资源的次数
JS/CSS 数量不可太分散,避免一下发起太多的请求,必要将部分资源合并在一起,减少请求的数量。但是在合并的过程中需求在体积和数量之间权衡,并不是越少越好,可将最大的体积控制在一个范围内进行合并
部分小体量级别的 JS/CSS 可内联到 HTML 中,减少请求数量
减小预检请求 OPTIONS 的发起,可通过服务端设置 Access-Control-Max-Age 字段或改为发起简单请求
取消无效请求,表单提交频繁点击,路由切换时还有未完成的请求。这些都会产生无效请求,对服务器和用户体验都是不好的
缓存策略
开启 http 强缓存与协商缓存,对于不同类型的资源使用不同的缓存策略
静态资源开启 CDN 服务
对于不常变化的数据包括外部 JS/CSS 资源,可进行前端浏览器缓存,减少请求,但此类缓存需设定好清除及更新的机制
3. 其他资源优化
图片 webp 使用,对于支持的设备使用 webp
图片裁剪,针对使用场景进行相应的裁剪
大图不要打包在项目中,上传到单独的静态资源服务器或是 CDN 中
图片上传前进行压缩,切记不要使用原图
设置图片标签尺寸大小,防止图片加载中导致页面布局抖动,影响 CLS 指标的数值
超出屏幕外的图片开启懒加载
对于项目中大量的小图标可使用 iconfont 字体方案
使用第三方字体库时尽可能按需文字生成
加载字体的时候会导致页面文字有一定的闪烁抖动,可在进入需要用到的页面前使用 preload 提前进行加载
页面渲染时
开启骨架屏,提升用户体验,避免加载到渲染过程中都是白屏阶段
对于大量列表的滚到使用虚拟列表
尽量多使用 CSS3 动画
使用 requestAnimationFrame 监听帧变化,使得在正确的时间进行渲染
合理使用 CSS,避免通配符,最大化样式继承,少用标签选择器,减少过深嵌套等
用户界面交互
减少页面重排、重绘
防抖节流的使用
合理使用 requestAnimationFrame 动画代替 setTimeout
开启 GPU 加速,CSS 中可使用以下属性(CSS3 transitions、CSS3 3D transforms、Opacity、Canvas、webGL、Video)来触发 GPU 渲染
减少 JavaScript 脚本执行时间,把一些和 DOM 操作无关且耗时的任务放到 Web Workers 中去执行
对未来某个时间内需要执行动画的元素,将其标记为 will-change,这样渲染引擎会将该元素单独生成一个图层
最后
本文对前端性能优化的方向列举了不少,除此之外也还有很多是没有涉及到的,比如小程序内,Vue/React 框架中特殊的其他优化,配合 App 原生能力优化等。以上优化方向的说明就较为简洁,具体的实操及原理有兴趣的同学可以多研究,面对这么多的方向优化,究竟如何选择呢。
没有所谓的绝对优化,都需要结合当前项目的应用场景及对项目全量的性能分析,找到某个方向的不足,针对性的优化,选择合适的方案。希望大家都能找到自己合适的优化方向,把项目优化的妥妥的。看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~
专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)
参考
版权声明: 本文为 InfoQ 作者【南城FE】的原创文章。
原文链接:【http://xie.infoq.cn/article/223c79d5a6e035b32ee57a79e】。文章转载请联系作者。
评论