前端性能优化-加载流程

本文最后更新于 2024年4月11日 上午

  越是交互复杂、用户量大的业务,对性能的要求就越是严格。大多数的前端性能优化,都是从页面的启动和加载流程开始梳理和定位,对于功能复杂的业务来说,这样的梳理尤为重要。

常见的页面加载流程

  1. 网络请求,服务端返回 HTML内容
  2. 浏览器一边解析 HTML,一边进行页面渲染
  3. 解析到外部资源,会发起HTTP请求获取,加载 JavaScipt代码时会暂停页面渲染
  4. 根据业务代码加载过程,会分别进入页面开始渲染、渲染完成、用户交互等阶段
  5. 页面交互过程中,会根据业务逻辑进行逻辑运算、页面更新

资源获取

资源大小

一般来说,前端都会在打包的时候做资源优化,资源类型包括 html、js、css、图片等,优化的方向包括:

  1. 合理的对资源进行分包

  首次渲染时只保留当前页面渲染需要的资源,将可以异步加载、延迟加载的资源拆离。通常我们会在代码编译打包的时候做处理,比如使用 Webpack 将代码拆到不同的 bundle 包中。
2. 移除不需要的代码

  我们项目中常常会引入许多开源代码,同时我们自己也会实现很多的工具方法,但是实际上并不是全部相关的代码都是最终需要执行的代码,所以我们可以在打包的时候移除不需要的代码。现在基本大多数的打包工具都提供了类似的能力,比如 Tree-shaking。

  除此之外,如果我们的项目较大,使用和依赖了多个不同的仓库。如果在不同的代码仓库里,都依赖了同样的 npm 代码包,那么我们可能会遇到打包时引入多次同样的 npm 包的情况。一般来说,我们在管理依赖包的时候,可以使用peerDependency来进行管理,避免多次安装依赖、以及版本不一致导致的多次打包和安装等情况。

  1. 资源压缩和合并

  代码压缩也常常是在打包阶段进行的,包括 JavaScript 和 CSS 等代码,在一些情况下也可以使用图片合并(雪碧图的生成)。通常也是使用的打包工具以及插件自带的压缩能力,开启压缩后的代码可能比较难定位,可以配合 Sorce Mapping 来进行问题定位。

  除了打包时的压缩,我们在页面加载的时候也可以启用 HTTP 的 gzip 压缩,可以减少资源 HTTP 请求的耗时。

资源缓存

资源缓存的优化,更多时候跟我们的链路有关

  • 减少 DNS 查询时间,比如使用浏览器DNS缓存、计算机 DNS缓存、服务器DNS缓存
  • 合理使用 CDN 资源,有效减少网络请求耗时
  • 对请求资源进行缓存,包括但并不限于使用浏览器缓存,HTTP缓存、后台缓存、比如使用 Server Worker、 PWA 技术

我们观察获取链路,获取出了大小和缓存的角度以外,还可以做更多的优化,比如:

  • 使用 HTTP/2、HTTP/3, 提升资源请求速度
  • 对请求进行优化,比如多个请求进行合并,减少通信次数
  • 对请求进行域名拆分,提升并发请求数

资源加载

流程加载拆分

页面加载过程,会分为: 页面可见、页面可交互:

  1. 页面可见

  页面可见可以分为部分可见内容和完全可见内容,对于部分可见内容,一般来说可以做 loading 的展示或者是直接展示,让用户知道页面在加载中,而非无响应。

  对于完全可见页面,则是用户可视区域的内容完全渲染完毕,除此之外,当前可见范围以外的内容,则是可以抽离出来离屏加载或者是懒加载的方式进行异步加载。

  1. 页面可交互

  同样的,页面可交互也可以分为部分可交互以及完全可交互。

   一般来说,组件的样式渲染仅需要 HTML 和 CSS 加载完成即可,而组件的功能则可能需要加载具体的功能代码。对于复杂或是依赖资源较多的功能,加载的耗时可能相对较长。在这样的情况下,我们可以选择将该部分的资源做异步加载。
在初始的内容加载完毕之后,剩下的资源需要延迟加载。对于页面功能完全可交互,同样依赖于分包资源延迟加载。加载流程的优化,不管是页面可见,还是页面可交互,都离不开延迟加载。
延迟加载可分为两种方式进行加载:懒加载和预加载。因此,资源懒加载和预加载也是加载流程中很重要的一部分。

资源懒加载

   我们常说的懒加载其实又被称为按需加载,顾名思义就是需要用到的时候才会进行加载。通过将非必要功能进行懒加载的方式,可以有效地减少页面的初始加载速度,提升页面加载的性能。

  常见的场景比如某些组件在渲染时不具备完整的功能,当用户点击的时候,才进行对应逻辑的获取和加载。遇到点击时未加载完成的情况下,可以通过适当的方式提示用户功能正在加载中。资源懒加载常常也是跟资源分包一起进行,大多数前端框架(比如 Vue、React、Angular)也都提供了懒加载的能力,也可以配合 Webpack 打包 做处理。

资源预加载

  资源预加载也称为闲时加载,很多时候我们可以在页面空闲的时候,对一些用户可能会用到的资源做提前加载,以加快后续渲染或者操作的时间。

  仔细一看,资源预加载和资源懒加载都比较相似,都会通过将资源拆离的方式做成异步延迟的方式加载。两者的区别在于:

  • 懒加载的功能只会在需要的时候才进行加载,因为一些功能用户可能不会使用到,比如帮助中心、反馈功能等等
  • 预加载的功能则是在不阻塞核心功能的时候,尽可能利用空闲的资源提前加载,这部分的功能则是用户很可能会使用到,比如获取下一屏页面的内容数据

复杂场景下的加载流程

复杂加载流程管理

  对于页面初始化流程过于复杂的应用来说,我们可以对加载流程做任务的拆分,分阶段地进行加载。

  举个例子,假设我们需要在 Web 端加载 VsCode,那么我们可能需要考虑以下各个功能的加载。

1
2
3
4
5
6
7
8
9
10
11
- 整体页面框架
- 顶部菜单栏
- 左侧工具栏
- 底部状态栏
- 文件目录栏
- 文件详情
- 内容展示
- 编辑功能
- 菜单功能
- 搜索功能
- 插件功能

以上只是我按照自己想法粗略拆分的功能,我们可以简单分成几个加载阶段:

a. 页面整体框架加载完成。此时可以看到各个功能区域的分布,包括顶部菜单栏、左侧工具栏、底部状态栏、项目内容区域等等,但这些区域的内容未必都完全加载完成。

b. 通用功能加载完成。比如顶部菜单栏、左侧工具栏、底部状态栏等等,一些具体的菜单或是工具的功能可以做按需加载和预加载,比如搜索功能。

c. 项目内容相关框架加载完成。此时可以看到项目相关的内容区域,比如文件目录、当前文件的内容详情等等。

d. 插件功能。用户安装的插件,在核心功能都加载完成之后再获取和加载。

  当我们根据项目的具体加载过程做了阶段划分之后,则可以将我们的代码做任务拆分,可以拆分成串行和并行的任务。串行的任务比如按照阶段划分的大任务,并行的任务则可以是某个阶段内的小任务,其中也可以包括一些异步执行的任务,或是延迟加载的任务。

长耗时任务的拆离

  如果我们的应用中会有耗时较长的计算任务,比如拉取回来的数据需要计算处理后才能渲染,那么我们可以对这些耗时较长的任务做任务拆分。

  同样的,我们还是回到 Web 端加载 VsCode 的场景。假设我们在加载某个特别大的文件,则可以考虑分别对该文件的内容获取、数据转换做任务拆分,比如分片获取该文件的内容,根据分片的内容做渲染的计算,计算过程如果耗时较长,也可以做异步任务的拆分,甚至可以结合 Web Worker 和 WebAssembly 等技术做更多的优化。

读写分离

  对于复杂交互场景,需要加载的资源较更多情况下,如果用户的权限只是可读,那么对于编辑相关的功能可以做资源拆分,队友有权限的用户才能进行编辑能力的记载。

   读写分离其实属于资源拆分的一种具体场景,我们结合业务的具体场景做具体的功能拆分,如果管理权限和只读权限。


前端性能优化-加载流程
https://dev.dgdream.online/2024/04/07/前端性能优化-加载流程/
作者
执念
发布于
2024年4月7日
更新于
2024年4月11日
许可协议