{T}

性能优化方案

对一个产品来说,用户体验是最重要的。当页面加载时间过长、交互操作不流畅时,会给用户带来很糟糕的体验,最坏的情况下会导致用户流失。因此,在各种技术优化方案中,性能优化出现的频率最高、优先级也常常更高。

对于前端应用来说,页面加载耗时、渲染耗时、网络耗时、脚本执行耗时等指标会影响用户的等待时长,而 CPU 占用、内存占用、本地缓存占用等可能会导致页面卡顿甚至卡死。因此,性能优化可以从常见耗时资源占用两个维度来解决。

常见的性能优化方案

对于前端应用来说,页面加载耗时、渲染耗时、网络耗时、脚本执行耗时等指标会影响用户的等待时长,而 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 耗时,预估该技术点的优化效果如何。

最后,根据每个优化点的优化效果以及相应的工作量评估,以预期为目标,选择性价比最优的技术方案。

在技术方案确定后,则需要对工作内容进行排期,并按计划执行。优化完成后,还需要结合目标和预期,对优化效果进行复盘,同时还可以提出未来优化的规划。

业界性能优化实践

百度:超级 App 的 Native 重性能方案

手百前端团队根据 QA 专项测试和用户反馈发现问题,定义指标、客户端和前端上报,用天幕平台监控预警,分析优化并评估。

  • 核心指标:FCP、FMP、卡顿率。
  • 优化思路:看 WebView 初始化能否优化、串行逻辑能否并行、渲染速度能否提升。
  • 落地方案CloudHybrid(SSR + 预加载 + 拦截请求,简化渲染流程、提前并行化网络请求)+ WebView 预创建(提前创建放入缓存池,展示时直接取出)。
  • 特点:依托 Native 能力,端内 H5 无需开发即可全面接入;缺点依赖 NA,升级需客户端发版,分享到端外需 H5 承接。

阿里云:标准化的性能监控与低风险 SSR

阿里云 ARMS 性能监控方案通过页面打开速度监控 Web 场景。

  • 核心指标:性能指数 Apdex =(满意数 + 可容忍数 / 2)/ 总样本量。用白屏时间 T 计算(默认 2s),满意为 0T,可容忍为 T4T,不满意为 >4T。
  • 首屏计算:用 MutationObserver 监听 DOM 变化,深度优先遍历计算元素得分(可见得 1 分),父元素可见则子元素不用计算,提升采集脚本性能。
  • 接入方式:支持 CDN 接入、NPM 包接入。
  • 双十一优化:针对复杂网络、低端 Android、外部 H5 调起、缓存过期重复渲染等问题,用数据预加载、缓存、SSR 解决。其 SSR 提供平滑自动切换方案(遇到问题切回 CSR 实现低风险),通过统一 FaaS 服务低成本接入,将秒开率提升到 82.6%。

美团:EnhanceHybrid 零白屏方案

美团用 Hybrid 开发模式,通过 EnhanceHybrid、SSR 和离线化技术优化。

  • EnhanceHybrid 是视图切换的预加载方案:用户触发 A 到 B 的跳转后,系统隐藏式开启新的 WebView 并展示加载动画,当 B 加载渲染完成后再展示 A。解决"无法提前加载 B、无法获取 B 渲染进度、A 等待时间长"三个问题。
  • 该方案可直接接入使用,不需要业务前端改动。

三家方案对比

公司核心技术特点
百度CloudHybrid + WebView 预创建依托 Native,重性能方案
阿里云ARMS(Apdex)+ 低风险 SSR标准化、指标采集前沿
美团EnhanceHybrid + SSR + 离线化低成本、务实解决 Hybrid 性能

性能优化本质是解决业务问题:手百属于超级 App,借助 Native 能力做重性能方案值得;阿里云提供标准化服务,指标采集和 SSR 实现较前沿;美团立足 Hybrid 用 EnhanceHybrid 低成本务实解决问题。

多端性能演进

  • RN/Flutter:跨端框架将渲染交给原生或自绘引擎,相比 H5 有更好的滚动流畅度和动画性能,但初始化加载较重,需结合离线包、预渲染优化首屏。
  • 小程序:采用双线程模型(逻辑层/渲染层分离),需注意 setData 频率对性能的影响。
  • EnhanceHybrid 等增强混合方案:在保留 H5 发版效率的同时,通过预加载、视图切换优化逼近原生体验。

选择方案需结合业务场景:内容型/强交互用 RN/Flutter,追求发版效率与多端覆盖用 H5/小程序,超级 App 内嵌页用增强 Hybrid 方案。