概述
上周星期四(2021/12/09)开始陆续有供应商反馈进入商家报价页面,卖家中心页面公共模块(菜单栏)存在卡顿问题,但是开发与测试在转测、转演、灰度阶段均未发现该性能问题。
分析
首先,我们需要了解页面的实现架构,卖家中心商家报价页面的公共模块ftl
与右侧内容区ftl
是通过Sitemesh
框架整合而成的,公共模块ftl
只定义了<div id="seller-topbar"></div>
与<div id="seller-sidebar" class="menus"></div>
的空架子,其元素内容通过引入执行seller-react-frame.js
将元素内容注入到相关元素标签中。
另外,注入的元素内容并不是在seller-react-frame.js
中实现的,而是在这个脚本中引入了其他脚本,引入的这些脚本是React
实现topbar、slider
组件后经webpack
构建生成的。
右侧内容区是切切实实使用freemarker
模板引擎实现的,数据由后端注入到页面中,相当于服务端渲染。
了解请求页面情况后,我们通过network
和performance
性能分析看看
network分析
从network
面板可以看出,公共模块中的一个脚本原始大小有1.3M
,gzip
压缩后传输大小为375KB
,从disk cache
(磁盘缓存)读取耗时2.78s
,清除浏览器本地缓存从CDN
请求耗时2.88s
,而公共模块其他的脚本原始大小都很小,只有一点几KB
,请求耗时只有一两百毫秒。
显而易见,这个脚本过大了,需要做代码拆分。另外,从disk cache
中读取也很耗时,改用走memory cache
(内存缓存),因为memory cache
更快。
内存缓存存储在 RAM 上,写入和访问速度更快,但会在计算机关闭或某些其他情况下被清除。磁盘缓存写入硬盘,读写速度较慢,但会留在磁盘上。
performance分析
再通过Performance
检测页面性能发现公共模块(菜单栏)脚本任务前面有一个执行耗时很长的任务,这个任务的功能是初始化四级地址和品牌选项,四级地址树使用ZTree.js
处理的,里面init
方法初始化树节点的事件复杂度比较高,因此我们看到检测到的执行时间较长(接近3秒) ,因此堵塞了公共模块脚本任务的执行。
鉴于业务需求,四级地址树节点可以不用在刚进入页面时初始化,因此我将其脚本请求和执行放在了onload
函数中,但是通过上图发现四级地址初始化还是先于渲染菜单栏执行,这是为什么呢?
网络请求面板看一下脚本获取的先后顺序,发现公共模块脚本先于四级地址脚本获取,这使我更加困惑了。这时想到左侧菜单栏是React
实现的,菜单栏数据是通过接口获取的,而不是像freemarker
模板引擎是通过后端注入数据的,然后排查获取菜单接口发现,为了获取到菜单栏数据,竟然存在三个接口的串行调用,因为接口的串行调用导致渲染菜单栏的任务晚于四级地址初始化的任务加入到任务队列中,再加上四级地址初始化耗时较长,从而引起用户感知左侧菜单栏卡顿。
解决方案
(1)对公共模块中1.3M
的脚本做代码拆分;
(2)优化脚本中的接口串行调用;
(3)置后四级地址初始化
(4)从disk cache
读取改成从memory cache
读取(猜想,还没找到实际方案);
(5)因为网络请求耗时不稳定,无法确保渲染菜单栏事件循环先于四级地址树节点初始化事件循环,因此可以在请求菜单栏接口成功后通过postMessage
或者自定义事件通知初始化四级地址树节点;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。