往期推文全新看点(文中附带全新鸿蒙5.0全栈学习笔录)
✏️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
✏️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
✏️ 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
✏️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
✏️ 记录一场鸿蒙开发岗位面试经历~
✏️ 持续更新中……
概述
Web加载完成时延是从页面请求开始到页面视口内容加载完成的耗时,建议该时长控制在 900ms以内,较低的加载完成时延能让用户感知到页面加载响应及时不卡顿。本文主要介绍了Web页面的加载流程及关键Trace点、性能分析工具、加载完成时延分析方法、并总结了常见导致加载完成时延过高的原因与解决方案。
Web加载流程
Web页面加载流程包括Web组件初始化,请求对应的网页资源后解析HTML与CSS文件、执行JS脚本构建出渲染树,同时网络进程会并行下载其他资源,然后系统会根据渲染树进行布局计算,确定每个元素在页面中的大小与位置,通过光栅化将几何信息转化为像素信息最后合成送显,对应泳道图如下图所示。
图1Web页面加载泳道图
表1Web页面加载关键Trace点
编号 | Web网页加载流程拆解 | 关键Trace |
---|---|---|
① | 点击事件 | 最后一个DispatchTouchEvent到组件初始化前 |
② | Web组件初始化 | H:NWebImpl |
③ | 导航流程 | NavigationControllorImpl::LoadURLWithParams到NavigationBodyLoader::OnStartLoadingResponseBody结束 |
④ | DOM&CSSOM解析 | CSSParserImpl::parseStyleSheet和ParseHTML解析,扣除HTMLDocumentParser::RunScriptsForPausedTreeBuilder |
⑤ | 等待网络资源下载 | Render主线程ThrottlingURLLoader::OnReceiveResponse前的空闲 |
⑥ | JS编译与执行 | EvaluateScript 和 v8.callFunction |
⑦ | 绘制 | ThreadProxy::BeginMaiFrame扣除v8执行 |
⑧ | 光栅化&合成 | 从ProxyImpl::NotifyReadyToCommitOnImpl开始到SwapBuffers结束 |
⑨ | 点击响应结束点 | NotifyFrameSwapped,UnloadOldFrame/第一个SkiaOutputSurfaceImplOnGpu::SwapBuffers |
⑩ | 完成时延结束 | 最后一个SkiaOutputSurfaceImplOnGpu::SwapBuffers |
Web加载性能分析工具
DevEco Profile
DevEco Profiler是DevEco Studio提供的场景化调优工具,开发者可通过该工具来确定Web加载完成时延的大小。
DevTools
DevTools是一个Web前端开发调试工具,提供了在电脑上调试移动设备前端页面的能力。
Web加载性能分析方法
图2Web加载完成时延分析流程
- 确定是否存在时延问题。开发者可使用DevEco Profiler或录屏工具辅助分析,确认Web组件内是否存在加载完成时延问题,若存在问题则执行后续分析逻辑。
- 确认关键性能瓶颈。使用DevTools进行分析,通过关注Devtools关键泳道与泳道内出现的关键性能问题,了解程序存在的耗时问题。
- 根据问题点选择合适的优化策略。根据程序存在的耗时问题,选择合适的优化方法进行优化。
- 查看优化后效果。优化后重新抓取Trace查看优化效果,查看是否达成加载完成时延不高于900ms,若未达成则从其他方向继续优化。
使用Profiler确认加载完成时延
- 使用Profiler抓取Trace并根据场景确认起止点。
- 确定Web加载完成时延Trace起点,点击切换到新的Web页面以DispatchTouchEvent, type=1为起点,而Web页面初始化加载则以H:NWebImpl | CreateNWeb(初始化Web组件)为起点,该Trace点位于应用主线程泳道内(该泳道负责应用主逻辑、接收多模信号、生成帧、分发子信号等),对应图3中的红色旗标处。
图3Web页面初始化加载起点
2. 确定Web加载完成时延Trace结束点,最后一个SkiaOutputSurfaceImplOnGpu::SwapBuffers为终点,该Trace点位于CompositorGpuTh泳道内(负责GPU 光栅化处理,生成信号送图形子系统执行渲染),对应图4的紫色旗标处。
**图4 **Web页面初始化加载终点
- 缩小Trace图并找到终点与起点处,选中终点到起点范围的Trace图,可查看当前Web页面的点击完成时延,若区域内加载完成时延大于900ms,通过下文的DevTools进行性能分析。
使用DevTools分析耗时区域
开发者可通过DevTools中的Performance选项卡录制Trace,根据录制后Trace进行分析,分析流程如下。
- 确认泳道起止点,起点为黄虚线处(0ms位置),终点为视口内容全部渲染上屏。
图5确定泳道起始点
图6确定泳道结束点
- 常用泳道概览。DevTools提供了多个泳道为开发者提供性能分析数据,常用泳道如下图所示。
由于Web加载完成时延主要问题集中在静态资源请求与主线程任务执行,所以需要重点关注如下泳道。
* Main(主要)泳道:显示主线程上的任务活动情况,包括脚本执行、样式计算、布局和绘制等。
* NetWork(网络)泳道:显示页面在加载过程中发出的所有网络请求,帮助开发者分析页面加载性能,找出加载缓慢的资源以便进行优化。
* Frame(帧)泳道:显示每一帧的渲染情况,包括帧率与渲染时间,可以检测到页面中的卡顿和掉帧现象。
* Animation(动画)泳道:显示动画的执行情况。
- 根据Web加载阶段进行分析,查看每个阶段内是否存在对应的常见问题。
各阶段异常根因分析及优化方法
以下按照 Web加载流程 进行区域划分,并梳理了常见问题产生的阶段与对应解决方案。
DOM&CSSOM解析(分析Main泳道)
常见问题
- 页面渲染阻塞,尤其是由于同步JavaScript的加载。该问题需要开发者了解源码,并在代码中使用异步加载或延迟加载JavaScript脚本。
- CSS文件加载缓慢,导致内容无样式或页面闪烁。该问题会在页面渲染时观测到,主要表现为页面先出现无样式效果再缓慢出现样式或页面内容出现快速且不稳定的显示变化。常见的解决方案有将CSS内嵌到HTML减少外部CSS请求数量,压缩CSS文件,使用CDN加速CSS文件加载、使用缓存等。
- 除以上针对特殊问题的优化方案外,还有其他通用优化方案,如预渲染、同层渲染等。
网络资源下载(分析NetWork泳道)
常见问题
- 某些网络请求阻塞UI渲染,存在关键资源加载慢的问题。此类问题需业务侧根据页面显示的诉求,分析出关键影响页面完成时延的请求,并使这些请求能够快速得到满足,例如使用CDN、预取POST请求等。
图7网络请求阻塞UI渲染示意Trace图
- 网络请求的数量过多、服务器响应网络请求过慢、无强依赖关系接口串行请求。此时通用的优化方案如下。
- 懒加载,减少文件大小,提高加载速度。
- 合并和压缩CSS、JavaScript等资源文件,减少请求数量。
- 使用浏览器缓存和CDN来缓存静态资源,减少不必要的请求。
- 懒加载非首屏内容或用户交互后才需要加载的内容,减少初始请求数量。
图8网络请求数量过多示意Trace图
图9服务器响应网络请求过慢示意Trace图
图10无强依赖接口串行请求示意Trace图
JS编译与执行(分析Main泳道)
常见问题
- 主线程任务执行稀疏,频繁的任务切换和上下文切换。如下图,可以看到红框内任务执行情况非常稀疏,但是红框右侧又有任务执行,说明该区域内主线程任务执行受到了其他方面的阻塞,一般是网络请求过慢或定时器导致任务执行滞后,建议查看空白区域所对应时间范围的网络请求来确定是否存在网络请求缓慢问题。
- 长任务阻塞UI渲染。JS脚本执行时会阻塞HTML解析,过长的脚本执行会导致页面持续白屏或显示未渲染完成的内容,脚本执行时长过长有多种可能,如脚本过大、脚本内算法时间复杂度过高等,此时需要开发者排查对应的脚本内容,延后不必要的脚本执行,优先保障视口内的内容加载。可采取的通用方案包括:预编译JavaScript生成字节码缓存、减少冗余JavaScript代码,推迟非必要JavaScript代码执行,利用代码分割只加载当前页面需要的代码等。
优化实践案例
案例一
问题描述
某应用详情页面加载完成时延高于900ms,实测加载完成时延2351ms。
问题Trace
加载流程分析
图中标号为页面加载流程顺序,应用侧请求到网页地址后,进入网络资源下载(对应区域①的网络泳道),此时观察到网络泳道出现大量的资源文件请求,同时页面持续白屏(观察页面截图区域),可以得出应用侧存在初始页面资源加载时网络并发过多的问题,然后可以看到请求下来的资源在主线程上执行(区域①的主要泳道),执行过程中发出网络请求请求到区域②的接口,请求过程中publishDetailv2()接口耗时较长,请求结束后执行请求的回调函数(对应主要泳道的区域③),该回调中接着请求了一个网络请求(对应网络泳道的区域④),同样可以看到有一个长时请求getPublishDetailRecommendList(),请求执行完成后执行对应的回调函数(区域⑤的主要泳道)接着触发了大量的网络请求(区域⑥与区域⑧)。
根因分析
- 初始加载时大量的CSS和JS加载、耗时530ms左右(区域①)。
- 部分接口串行请求(区域②中的publishDetailv2()与getPublishDetailRecommendList()),主线程任务稀疏(区域②、区域④对应的主要区域任务火焰图)。
- getPublishDetailRecommendList()接口网络等待与内容下载时间过长,一次加载了大量数据(区域④)。
- 加载了大量的图片(网络泳道内的绿色区域)、且图片之间的串行加载等待和下载非常耗时(区域②、④、⑥、⑧)。
优化方案
- 根据业务需求选择尝试合并与压缩JS与CSS文件,减少网络请求个数与大小,检查代码中是否有非必要首屏加载的CSS与JS文件,延后此类文件加载时机。
- 预取POST提前网络请求。提前publishDetailv2()与getPublishDetailRecommendList()请求。
- 优化getPublishDetailRecommendList()接口请求,采用分页加载,一次只加载固定数量的数据。
- 优化页面组件加载图片的逻辑,页面显示范围有限但是一次性加载了大量的图片资源,确定业务逻辑上所有图片资源是否都需要首屏加载。
- 头部Banner组件可加载首张图、显示完后再加载后续图。
- 底部列表组件可使用占位图,滑动期间再加载图片。
- 其他通用优化方案。
- 对网页进行 预渲染 提前拉起渲染进程。
- 使用CDN内容分发加速静态资源请求(图片、脚本文件等)。
- Web缓存(可优化200ms左右)此页面为模块详情页、页面布局的CSS和JS为固定样式页面HTML、CSS、JS可缓存本地,后续打开页面可缩短加载时延。
案例二
问题描述
某应用优惠券详情页面使用Web组件加载,实测加载完成时延为1552ms,远高于900ms。
问题Trace特点
加载流程分析
根据Trace图可以看到,页面开始加载后请求到一个接口以及一段JS脚本,该脚本引出了大量的网络请求以及任务回调执行。屏幕持续白屏(区域①到600多ms处)等待JavaScript脚本执行,执行完成后页面进入骨架屏(区域③),此阶段有多个长排队请求,同时可以看到请求到服务器后,服务器处理时长较长(浅灰色区域)。
- getUserInformation()接口耗时1.2s,其中在等待520ms,请求耗时680ms。
- getHisComboMealResource()接口耗时1.9s,其中等待630ms,请求耗时1.3s。
根因分析
- JS执行阻塞了首屏加载,加载阶段耗时长(达到了600多ms的空屏)。
- 接口请求耗时过高(图中网络泳道内浅灰色区域为请求发出等待响应的时间,可看到大部分接口等待响应时间都比较长,可能有硬件限制),导致主线程加载阶段任务稀疏,影响整体完成时延达成。
优化方案
- 此页面为固定页面可考虑Web预加载、可首页处预加载Web,跳转后快速渲染。
- 可从如下方面尝试优化网络请求。
- 网络请求数量优化,减少接口数量合并网络请求,从而降低服务器网络负载。
- 后端检查getHisComboMealResource()耗时较高的原因,优化后端处理逻辑。
总结
本文提供了Web页面加载时延分析与优化的方法,涵盖了Web页面加载流程、性能分析的工具与方法,并通过两个案例结合分析方法进行实操,帮助开发者掌握Web页面加载性能提升的方法。