背景
去年此时,云安全产品1秒战役拉开序幕。1s?控制台产品的启动时间要在1s内完成?所有JS一般在8M左右(甚至更大),1M的JS不算加载时间,解析+编译+执行的时间在一般配置的PC上要0.6秒~1秒(如图)。平时开发用的电脑配置较高,我们必须意识到有80%的用户电脑至少比我们的慢2倍以上。平时还是要多在降速的情况下用用我们的产品。体验优化和系统优化特点不同,物理值不能完全代表体验,比如白屏1秒,然后页面全部出来,这不是一种好的体验。还是要追求感知指标,于是有了PCP指标。
sas-js-cost.png
PCP(Perceivable Contentful Paint)-- 是阿里云定义的用户感知导向的前端性能指标,基准值是1.5s。和基于绘制的度量指标区别在于--追求用户感受上的快,理论上比Web Vitals的LCP靠前一些,因为要保证数据的时效性,允许等待数据的加载。“1秒”实际上是设计并实现一种体验策略。快,是最重要的用户体验之一。速度和用户感受的关系(如图),体现在用户打开和使用产品的整个会话周期中。在我们的方案中,我们把“1秒”体验升级为“0秒”体验--加载阶段是0秒打开,运行时阶段是0秒卡顿。当然0s不是物理绘制上的0s,是用户感觉上的0s。


我们从没把“前端性能优化”当成简单的“优化”来搞。云安全这次有8款产品要达标,肯定要下一套通用的方案,并且要和现有的前端架构充分融合,成为前端工程体系的一部分。不仅快、持续快,而且默认快。变快有两种有效的思路:SSR 和 PWA。我们从一开始就坚定的选择了后者。我们是做前端的,首先要把浏览器端的能力发挥到极致再说。PWA(Progressive Web Apps,渐进式的Web应用)应该怎么玩,策略设计重于实现,一是SW有坑,二是不能损害控制台数据的时效性,所以不能简单的按常规套路来。利用PWA完全可以达到“0秒”体验,实际上小于300ms(眨一下眼睛的时间大约是750ms),用户感受就是“0秒”。也就是输入地址敲回车,页面立刻出现,就是要这种感觉。体验一下云安全中心现在的效果(如图),最近7天平均639ms,彻底上了SW还能减掉200ms左右的页面响应时间,进入400ms就是“0秒”效果了。

(本文将以云安全中心为例做具体的讲解)
效果

页面渲染管线大体上如图所示:一是非常轻的服务端负载为的是TTFB足够快,做了两级缓存优化。二是利用Service Worker底层的Cache Storage实现高精度的内容区骨架屏。三是统一的资源管理,按需加载,加速初始UI的渲染。整体的渐进效果要做到平滑连贯,上图中右侧是一个微应用,用户丝毫不会察觉加载的痕迹。方案对业务代码的开发几乎无侵入,全部在工具侧和平台侧实现。

结果
成绩:入围1s战役的云安全的8款产品,从2021年6月到近期的结果如下:
云安全中心3.3s↘︎0.63s证书服务1.85s↘︎0.61s
云防火墙3s↘︎0.56sDDoS防护2s↘︎0.56s
WAF2.7s↘︎0.66s实人认证2.2s↘︎0.6s
风险识别2.6s↘︎0.69s内容安全2s↘︎0.67s
趋势:回顾云安全中心的整个历程,从3秒一路下来,到去年8月达标,接下来做的是加载流畅度和运行时的优化。围绕数据→效果→数据,不断优化和细化方案。


PCP < 1s的用户占比不断提高,我们真切感受到对用户影响在不断扩大。当看到改变发生时,还是很有成就感的。
平稳:云安全中心最近30天代码变更约6.8万行 / 线上发布40次,PCP和TTFB躺得很平。

实现
优化首先围绕CRP(关键渲染路径)进行(如图)。包括文案在内的所有资源文件的HTTP缓存为强缓存,同时确保开启gzip。清理<head>仅保留初始渲染需要的CSS,移出JS。统一管理JS / CSS文件的加载,三方JS异步加载和延后执行。这套动作下来提升不是很明显,因为尚未触及关键问题。两个关键点:Service Woker缓存和按需加载。背后遵循的原则:“As little as possible, as early as possible”。
关键渲染链路优化

  1. 尽可能早:SW线程的启动时机是最早的,在页面请求之前,浏览器就会派发一个Fetch事件触发该页面内的SW,我们尝试对页面进行缓存。次早的是在页面前序没有堵塞的位置放置骨架屏,这要求页面的响应时间(TTFB)要尽可能的快。以云安全中心为例,TTFB=218ms,页面大小=20K,下载时间=28ms,再加上微乎其微的DNS解析和连接时间,共计不超过300ms。UI渲染往往要在高达几M的依赖文件之后执行,缓存虽然可以化解文件大小的问题,但对于体积这么大的JS来说,解析和执行的时间还是太长。所以需要尽可能早的渲染一个无依赖的骨架屏,优先加载关键资源文件。最后,数据请求的触发的时机还是太晚,在UI渲染时。预请求数据,可以进一步提高页面显示的速度。
  2. 尽可能小:对JS / CSS / 文案进行“瘦身”和移除冗余代码。通过对构建包进行分析,瘦身包括:
    1. 用轻量的day.js替代moment;
    2. Lodash按需引用;
    3. core.js编译时按需引用;
    4. 将React / React-Router / Dva / 图表库 / 组件库等版本稳定的公共库单独打包;
    5. 图表依赖的地图数据按需加载;
    6. 按路由进行code split;
    7. 微前端迁移。按路由进代码分割,主应用构建出来的size平均会减少50%,但公共依赖vendor文件不会小。完全迁到微前端的项目,主应用和依赖文件都会比较小。
治理冗余:文案和CSS的冗余问题比较突出。通过对线上文案使用情况的监控和分析大约有40%左右的冗余文案。
加载阶段的优化基本上是遵循PRPL模式进行的。


(来自Addy Osmani《The Cost Of JavaScript》)
浏览器端骨架屏方案
在PWA中骨架屏通常是一个纯静态的UI页面框架,但是在控制台的场景是多版本多语言多Region,UI差异较大。要抽出一个“骨架”也只能是一个相当简化的页面框架(左图),我们是在运行时根据不同的帐号生成相应的骨架屏(右图)。采取缓存优先的策略,首先从本地取骨架屏,如无显示默认骨架屏,有的话则显示。等到主应用的脚本执行时,覆盖骨架屏(水合效果更好,但是我们产品变化太快),同时更新本地缓存。所谓高精度的骨架屏是包含完整的样式,可以平滑过渡到真实UI。Cache Storage兼容性没问题,存储容量取决于客户设备可用的磁盘空间大小,一般范围是50MB到20GB。目前缓存一个页面的骨架屏~500KB,我们会自动清除超7天的缓存。通过配置只对主要的入囗路由进行缓存。
简单的骨架屏高精度的骨架屏
骨架屏的运行机制:
后续
这次1秒战役我们从追求“1秒”到追求“0秒”,充分利用浏览端的能力,探索了一套不同的实现方案。后续还有三件事待做,一是继续深入Service Woker完备B端产品PWA的方案,二是继续探索Webpack联邦模块,三是并发的数据请求方案。“1秒”之后,我们又做了大客户性能优化、白屏治理、“卡顿”问题的治理,丰富多维度的体验日志的采集,目前我们已把体验管理纳入到前端的核心工作中。历经一年的“1秒”战役已经结项,更多的将直接影响用户体验的项目正在路上。

“让改变发生”

关注「Alibaba F2E」微信公众号把握阿里巴巴前端新动向
继续阅读
阅读原文