前端性能优化与解决方案
为什么要进行性能优化呢?因为对于一个产品来说,用户的体验是最重要的。当页面加载时间过长、交互操作不流畅时,会给用户带来很糟糕的体验,最坏的情况下会导致用户的流失,到最后还会出现“代码写得好看又怎样,产品还不是一样难用”的情况。
正因为如此,在各种技术优化的解决方案中,性能优化出现的频率最高、优先级也常常是更高的。
所以,今天我会带大家了解前端常见的性能优化手段。首先我们来看看常见的性能优化方案。
常见的性能优化方案
对于前端应用来说,页面加载耗时、渲染耗时、网络耗时、脚本执行耗时等指标会影响用户的等待时长,而 CPU 占用、内存占用、本地缓存占用等可能会导致页面卡顿甚至卡死。因此,性能优化可以分别从常见耗时和资源占用两方面来解决。
时间角度优化:减少耗时
在课程的第一部分,我介绍了在浏览器页面加载过程中,可以分为以下阶段:
网络请求,服务端返回 HTML 内容;
浏览器一边解析 HTML,一边进行页面渲染;
解析到外部资源,会发起 HTTP 请求获取,加载 Javascript 代码时会暂停页面渲染;
根据业务代码加载过程,会分别进入页面开始渲染、渲染完成、用户可交互等阶段;
页面交互过程中,会根据业务逻辑进行逻辑运算、页面更新。
根据这个过程,我们可以从 4 个方面进行耗时优化:网络请求优化、首屏加载优化、渲染过程优化、计算/逻辑运行提速。
我们分别来看看。
1. 网络请求优化。
网络请求优化的目标在于减少网络资源的请求和加载耗时,可以参考以下优化方案:
减少 DNS 查询时间,比如使用浏览器 DNS 缓存、计算机 DNS 缓存、服务器 DNS 缓存
合理地使用 CDN,有效地减少网络请求耗时;
对请求资源进行缓存,包括但不限于使用浏览器缓存、HTTP 缓存、后台缓存,比如使用 Service Worker、PWA 等技术;
移除代码中无用的部分,比如使用 Tree-shaking、代码分割、移除用不上的依赖项等;
对请求资源进行合理的拆分(CSS、Javascript 脚本、图片/音频/视频等),减少请求资源的体积;
对资源进行压缩,减少传输数据大小;
使用 HTTP/2、HTTP/3,提升资源请求速度;
对请求进行优化,比如对多个请求进行合并,减少通信次数;对请求进行域名拆分,提升并发请求数量。
在请求资源返回后,浏览器会进行解析和加载,这个过程会影响页面的可见时间,通过对首屏加载的优化,可有效地提升用户体验。
2. 首屏加载优化。
顾名思义,首屏加载优化核心点在于:**将页面内容尽快展示给用户,减少页面白屏时间。**因此,首屏加载优化的方案主要包括两方面:首屏加载耗时优化以及使用页面过渡效果。
其中,性能和渲染耗时优化属于技术优化手段,可以通过以下方式进行:
对页面进行分片/分屏加载,将页面可见/可交互时间提前;
优化资源加载的顺序和粒度,仅加载需要的资源,通过异步加载方式加载剩余资源;
使用差异化服务,比如读写分离,对于不同场景按需加载所需要的模块;
使用服务端直出渲染,减少页面二次请求和渲染的耗时;
使用秒看技术,通过预览的方式(比如图片)提前将页面内容提供给用户;
配合客户端进行资源预请求和预加载,比如使用预热 Web 容器;
配合客户端将资源和数据进行离线,可用于下一次页面的快速渲染。
相比性能和渲染耗时优化,使用页面过渡效果可能更倾向于产品策略。很多时候产品策略的调整,给用户带来的体验优化效果不低于技术手段优化,因此我们也需要重视。常见的方案包括使用骨架屏进行预渲染,以及使用过渡动画让用户感知到页面正在顺利加载,从而避免用户对于白屏页面或是静止页面产生烦躁和困惑。
除了首屏渲染以外,用户在浏览器页面过程中,也会触发页面的二次运算和渲染,此时需要进行渲染过程的优化。
3. 渲染过程优化。
渲染过程的优化,主要在于减少用户的操作等待时间,避免出现卡顿的情况,比如:
使用资源预加载,在空闲时间,提前将用户可能需要用到的资源进行获取并加载;
减少 DOM 数量、减少/合并 DOM 操作,减少浏览器渲染过程中的计算耗时;
通过合理使用浏览器 GPU 合成,提升浏览器渲染效率;
使用离屏渲染,在页面不可见的地方提前进行渲染(比如 Canvas 渲染);
通过将页面渲染帧率保持在 60FPS 左右,提升页面交互和渲染的流畅度。
除此之外,渲染过程同样可以使用页面过渡动画的方式(比如加载中),给予用户及时的反馈,来提升用户的体验。
对于运算逻辑复杂、计算量较大的业务逻辑,我们还需要进行计算/逻辑运行的提速。
4. 计算/逻辑运行提速。
计算/逻辑运行速度优化的方式主要包括:
通过将 Javscript 大任务进行拆解 + 并行计算的方式,有效地降低整体计算耗时,比如使用 Web Worker;
通过使用运行效率更高的方式,减少计算耗时,比如使用 Webassembly;
通过将计算过程提前,减少计算等待时长,比如使用 AOT 技术;
通过使用更优的算法或是存储结构,提升计算效率,比如 VSCode 使用红黑树优化文本缓冲区的计算;
通过将计算结果缓存的方式,减少运算次数。
在前端性能优化实践中,网络请求优化和首屏加载优化方案使用频率最高,因为不管项目规模如何、各个模块和逻辑是否复杂,这两个方向的耗时优化方案都是比较通用的。
相比之下,对于页面内容较多、交互逻辑/运算逻辑复杂的项目,才需要针对性地进行渲染过程优化和计算/逻辑运行提速。
我们继续看看资源占用和卡顿的问题。
空间角度优化:降低资源占用
提到性能优化,大多数我们都在针对页面加载耗时进行优化,对资源占用的优化会更少,因为资源占用常常会直接受到用户设备性能和适应场景的影响,大多数情况下优化效果会比耗时优化局限。
资源占用常见的优化方式包括:
合理使用缓存,不滥用用户的缓存资源(比如浏览器缓存、IndexDB),及时进行缓存清理;
通过使用数据结构享元的方式,减少对象的创建,从而减少内存占用;
避免存在内存泄漏,比如尽量避免全局变量的使用、及时解除引用等;
避免复杂/异常的递归调用,导致调用栈的溢出。
对于页面耗时和资源占用的性能优化分析,可以使用 Chrome 开发者工具进行针对性的分析和优化,这些内容在上一讲已经介绍过了,这里就不再详细讲解。
那么,是不是知道了常见的性能优化方案,就可以直接在项目中使用呢?
如何在项目中进行性能优化
性能优化通常需要投入不少的人力和成本来完成,因此更多时候我们可以将其当作是一个项目的方式来进行管理。从项目管理的角度来讲,我们的性能优化工作会拆解为以下部分内容:
确定优化的目标和预期;
确定技术方案;
对工作内容进行排期,并按计划执行;
优化完成后,结合目标和预期,对优化效果进行复盘。
对于步骤 3、步骤 4,在最后的 27 讲中有详细介绍具体需要怎么做,所以这里我主要围绕优化目标和预期的确定,以及技术方案和工作内容的确认来进行介绍。
确定优化的目标和预期
性能优化的第一步,就是要确定优化的目标和预期。在给出具体的数据之前,我们首先需要对一些性能数据进行定义。比如:
网络资源请求时间。
Time To Start Render(TTSR):浏览器开始渲染的时间。
Dom Ready:页面解析完成的时间。
Time To Interact(TTI)):页面可交互时间。
Total Blocking Time (TBT):总阻塞时间,代表页面处于不可交互状态的耗时。
First Input Delay(FID):从用户首次交互,到浏览器响应的时间。
要选择合适有效的指标进行定义,比如由于前端框架的出现,Page Load 耗时(window.onload
事件触发的时间)已经难以作为页面可见时间的关键点,因此可以使用框架提供的生命周期,或者是使用 Largest Contentful Paint (LCP,关键内容加载的时间点)更为合适。
对需要关注的性能数据进行定义完成后,可以对它们进行目标和预期的确定,一般来说有两种方式:
对比原先数据优化一定比例,比如 TTI 耗时减少 30%;
通过对竞品进行分析确定目标,比如比竞品耗时减少 20%。
在确定了目标和预期之后,我们便可以根据预期来确定优化的方向、技术方案。
确定技术方案
根据确定的目标和预期,我们就可以选择合适的优化方案。为什么不能将上面全部的技术方案都做一遍呢?主要原因有两个:
一是性价比,可能部分技术优化需要投入大量的人力,但是优化效果可能不明显,比如切换到 HTTP/2 和 HTTP/3;
二是不适用,比如有些业务并不具备差异化服务。
举个例子,小明的预期目标是客户端内打开应用 TTI 耗时减少 30%,因此他可以选择的优化方案包括:
对首页数据进行分片/分屏加载;
首屏仅加载需要的资源,通过异步加载方式加载剩余资源;
使用服务端直出渲染(SSR);
使用 Tree-shaking 移除代码中无用的部分;
配合客户端进行资源预请求和预加载,比如使用预热 Web 容器;
配合客户端将资源和数据进行离线,可用于下一次页面的快速渲染。
其中,5、6 需要客户端小伙伴进行支持,那么小明则可以根据对方可以投入人力进行配合,来确定这两个优化点是否在本次方案中。
为了达成目标,对合适的技术优化点进行罗列之后,需要对每个优化点进行简单的调研,确定它们的优化效果。比如针对首页数据进行分屏加载,可以通过简单的模拟测试,对比完整数据的 TTI 耗时,与首屏数据的 TTI 耗时,预估该技术点的优化效果如何。
最后,根据每个优化点的优化效果以及相应的工作量评估,以预期为目标,选择性价比最优的技术方案。
在技术方案确定后,则需要对工作内容进行排期,并按计划执行。优化完成后,还需要结合目标和预期,对优化效果进行复盘,同时还可以提出未来优化的规划(这部分的内容可以参考 27 讲)。
小结
今天我介绍了前端开发中常见的性能优化方案,核心优化思想为时间上减少耗时、空间上降低资源占用。其中耗时优化在前端性能优化中更常见,优化方案包括网络请求优化、首屏加载优化、渲染过程优化、计算/逻辑运行提速四个方面。
了解了常见的性能优化方案,我们还需要根据项目自身的情况进行优化实践。在实践过程中,分别需要确定优化的目标和预期、确定技术方案、对工作内容进行排期和执行、优化结束后进行复盘。
最后给你留个作业:对一次性的春节红包活动页面,可以做哪些技术优化来提升用户体验呢?
Tip:需要考虑到高并发情况下的柔性降级哦。
把你的想法写在留言区~
# 精选评论
# **7512:
服务端渲染CDN预热,提前将静态资源文件部署到CDN(用阿里CDN的时候见过这个功能)高并发是指抢红包吗?如果是应对用户使劲刷新页面,可以使用强制缓存策略。如果是抢红包接口,可以负载均衡,可以在入口处拦截部分流量柔性降级不懂,是不是出错了要提示活动太火爆了,给个错误页面?前端初学者,请大佬斧正
# 讲师回复:
提到了很不错的几个点:合理使用CDN、柔性降级。另外还可以考虑页面的渲染耗时、页面交互体验等,以及是特大并发的情况下,为了避免后台雪崩,前端也可以根据情况做限频处理
# **宇:
首先直接先在cdn缓存里预热。一次性的页面技术栈不要太重,尽量不要有太多依赖,不要浪费太多时间在运行时。活动基本上只会在客户端,客户端需要缓存。如果第一次请求就遇到服务器压力太大或者用户自己线路故障,客户端可以采取超时降级策略,转而请求另外一个只保留最小可用功能的页面。还有一个最稳妥但是成本大一些的办法,过节前可以让客户端发一次版,活动页走客户端渲染,能分流一波用户。
# 讲师回复:
思路不错,不过一般高并发的瓶颈会主要出现在 CGI 请求接口上,可以针对性地做前端限频、用更加友好的用户提示等等