本文整理了近两年来国际站店铺在体验优化上做的努力和探索,也取得了不错的成果,分享出来希望对其他类似场景能有帮助。
性能优化大体上可以分为网络性能优化和交互体验优化:
- 交互体验优化,优化页面的交互体验,滚动是否顺滑,点击反馈是否及时,操作是否有卡顿等
- 避免 DOM 的重绘和重排
- DOM 操作尽量减少或者进行操作合并
- 动画元素尽量采用 absolute 等脱离文档流的定位
- 确保对用户的操作反馈及时,刷新率保持 60fps
- 保证每一个 js 任务在 1000/60=16.67ms 执行完毕
- 复杂计算交给 woker 进程
- 避免 DOM 的重绘和重排
- 网络性能优化,大多数情况是要进行首屏性能优化
- 减少 ttfb 的时间
- 减少 dns 查询、TCP 握手建链、ssl 建连
- 避免 302 redirect
- 减少后端 rt 时间
- 减少资源下载时间:比如减少 html 内容、开启 gzip 压缩等
- 减少前端阻塞资源加载时间
- 减少同步 css 资源
- 避免同步 js 标签
- 增加合适的缓存
- 通过 ServiceWorker 等手段进行提前加载和预渲染
- 减少 ttfb 的时间
具体到国际站店铺: 一个由用户后台搭建并生成前台场景的页面上,在优化上依然遵循上述方向。下面是我结合店铺实际情况后在体验优化上做的一些尝试(由于合规问题,一些业务数据进行了脱敏):
网络性能优化措施
PC 旺铺 - 装修后台图片优化
背景
店铺是一个可以卖家自己搭建页面的场景,很多卖家喜欢通过上传一个背景图来提升店铺的美观度。但这些图片的体积都非常大,一个 1920 * xxx 尺寸的图片体积都在 1M 以上,非 wifi 网络环境下加载比较慢。
方案
要对其优化并不能通过简单的图片压缩来解决,因为很多情况下压缩后都会导致图片失真严重,卖家就会提工单过来投诉。为此我们需要另一种方法来对图片体积进行缩减。通过观察可以发现,绝大多数情况下背景图中间部分大都被正常模块所覆盖,前台能看到都是中间模块展示宽度以外的内容。所以可以尝试用单色覆盖图片中间的部分,这样可以大大减少用户上传的背景图片大小。
实现的原理很简单:把背景图画到 canvas ,然后对其中间 1200px 宽度部分进行纯色填充,核心实现代码如下:
/**
* 用纯色填充指定区域
*/
coverArea (canvas) {
const { coverColor, coverWidth } = this.props
if (!this.state.needCover) {
return canvas
}
const w = canvas.width
const h = canvas.height
const ctx = canvas.getContext('2d')
const gap = (w - coverWidth) / 2
// 清除中间 1200px 部分的内容
ctx.clearRect(gap, 0, coverWidth, h)
ctx.fillStyle = coverColor
// 用给定的颜色填充中间 1200px 的内容
ctx.fillRect(gap, 0, coverWidth, h)
return canvas
}
经过测试:对于一张 1920 * 1080 的细节较多的风景图,在 jpeg 格式下,原图 1.6MB ,裁剪后为 464KB,缩减后为原来的 464/(1.6*1024)=0.2832 ,差不多为原图 30% 的体积,提升巨大,如下图:
优化后既可以提升页面打开速度,又可以减轻 CDN 流量消耗,一举两得。
PC 旺铺 - 三方模块用户性能优化
背景
由于目前三方模板1的静态资源走的是阿里云的 cdn, 域名为 shopmod-icbu.alicdn.com
,海外节点较少,资源加载慢,影响用户体验。
所以尝试通过采用拥有更多海外加点的 akama 服务商的 cdn 进行转发,从而加快海外用户访问使用三方模板商铺的访问速度,以提升三方模板商家的用户访问体验。
方案
将 shopmod-icbu.alicdn.com/1dbb3279357007484e7778d9a1060811/web-index.css
替换为 http://i.alicdn.com/@shopmod/1dbb3279357007484e7778d9a1060811/web-index.css
然后由 i.alicdn.com 进行转发。
旺铺前台的 js 文件都是由 shop-render
引用的 intl-mod-loader
控制异步加载的。 对于二方模块, ownType
为 *,资源地址由 shop-render
的 config 决定。 对于三方模块, ownType
为 ** ,资源地址由三方模块的 module-data
中 mds 的 assetsCss、assetsJs 决定,模块数据 assetsCss、assetsJs 地址在后端应用中进行修改。
效果
首屏在以前优化的基础上又减少了 12.4% ,停留时长也增加了 4.6%,蹦失率降低了 5.2% 。
PC 旺铺 - 模块异步化
背景
首页后端 RT 相较于整个店铺或者说国际站来说都有点长,最终影响了首屏速度。
分析
可以看到旺铺首页后端 RT 在 **0ms ,而整体 RT 在 **0ms 左右,所以减少首页的 RT 是一个入手点。为什么首页的 RT 远大于平均值,是因为首页支持用户自定义装修,模块数量不固定,而每个模块的渲染取树都需要花费时间。
之前跟后端同学一起追踪过整个渲染链路,发现推品相关模块后端取数时间较长,平均一个品类模块需要 **0ms 左右。那么如果一个用户首页装修了 10 个推品模块,那么单后端 RT 就需要至少 **0ms * 10 (是的,我们接入的中台渲染逻辑是所有模块串行渲染),加上 **0ms 的网络传输时间,那么 ttfb 就需要 **0ms ,所以我们采取的优化策略是对品类模块进行异步化。
结果
我们先对用户使用频率较高的模块进行了异步化改造,上线后有了不错的数据:首屏提升了 100ms 左右。
整体 rt 时间缩短,ttfb 降低了 12% 左右。
无线旺铺 - 从 weex 迁移到 react 同构
背景
老的无线旺铺是基于 weex 的异步渲染的页面,首屏 **s+,一顿复杂的优化操作下勉强优化到了 **s 左右,而且开发上存在很严重的效率问题。当时在做了深入的分析后,绝对定无线旺铺进行前端架构升级,从 weex 迁移到和 PC 店铺一样的 react 同构的 h5 店铺。
详细背景在公司内网写了一篇 《ICBU 旺铺前端架构现状以及演化方向》,里面涉及到合规问题就不放出来了。
方案
由于无线店铺的情况很复杂,有官方模块还有三方模块,官方模块有 50+ 个,三方模块更是数不胜数。所以无法一次性重构完毕再上线,需要一个能边跑边换轮子的方案。考虑到店铺的模块大多以纯展示为主,重交互的模块为辅,在这个背景下我提出了两个方案:
- rax 转换代码转换方案
- 顾名思义,就是把基于 rax 的 weex 代码,通过 babel 转码工具转换为 react, 加速模块迁移过程
- rax react 混合渲染方案
- 多余三方模块以及一些其他优先级不高的模块,通过混合渲染的方案直接在新架构下运行新老两种模块,解决模块太多无法迁移的问题
结果
整个项目分了两期来做:
- 一期项目对单个 List 页面进行了人工迁移,主要是为了验证 react 同构的 h5 页面是否能够满足业务需求
- 二期开始通过前面提到的两个方案,直接整个店铺进行迁移
成果上:
- 一期
- 性能:List 首屏性能从 ***s(优化后的 weex) 提升到了 ***s ,降低 39.7%
- 业务上:List 的滚动深度提升了 3.82%,List->Detial 提升了 4.3%,广义 ab 提升了 3.2%
- 二期
- 性能:整体首屏从 ***s 提升到了 ***s ,降低了 37.5%
- 业务:SEO 流量环比日均增加 29.075%,停留时长上升了 7.681%,广义 ab 上升了 18.673%
流式渲染
在做了一些常规优化方案后,店铺的 PC 和无线端的性能都进入了 ***s 以内,此时店铺的 ttfb 为 ***ms 左右,还有优化的空间。于是我们做了[流式渲染改造]:
流式渲染是利用 http1.1 的分块传输编码的特性, 让服务端分块返回 html , 浏览器可以在接收时逐步渲染页面, 这样有助于页面的首屏展现, 提升用户体验.
成果
PC 店铺和无线店铺先后都上了流式渲染, 由于后端 rt 的大幅下降,ttfb 的降低都在 ***ms 左右,具体成果如下:
- PC:
- 性能上:首屏降低了 8% 左右
- 业务上:由于当时上线没有做 abtest ,所以没有观察具体的业务数据
- M 站:
- 在性能上首屏减少了 8.69% 的时间
- 业务上:
- 平均访问深度相对提升了 1.024%
- 平均访问时长相对降低了 0.020%
- 一步 ab 转化相对提升 0.318%
- App:
- 首屏在 50% 灰度的情况下,首屏减少了 7.2% 左右
- 业务上:
- 访问深度相对降低 2.090%
- 访问时长降低 2.093%
- 一步 ab 转化相对提升 0.547%
同时产出了工程化接入流式渲染的方案 [[流式渲染工程化]]
交互体验优化
PC 旺铺 SPA 改造效果
背景
PC 店铺原本是一个传统的后端渲染的多页面体系架构, 点击每个 Tab 的时候需要做一次普通的页面渲染来完成页面的刷新. 虽然我们已经做了很多诸如流式渲染, 耗时模块异步等优化方案, 但每次页面切换都需要浏览器进行整个页面的刷新,这样的体验依然是不够好. 所以我们对 PC 店铺进行了又一次升级, 通过前端手段将其升级成为了一个伪单页应用.
方案
现有的渲染方案如下:
浏览器发起请求
-> 后端取数
-> 调用 React 同构服务进行模块渲染
-> 拼接同构结果为一个完整的字符串返回给浏览器
-> 浏览器解析加载静态资源并渲染页面
-> js 加载完成后用 React 重新刷新整个页面进行事件绑定等二次渲染
-> 渲染完成.
改造的方案如下:
成果
店铺的单页改造上线后 AB Test 对比,其中实验桶相对对照桶首屏降低了 5% 左右,ab_rate 相对提升 1.5%,蹦失率相对降低了 1.7% 。 后续预载功能上线后(没有做 AB Test),更是进一步提升了性能,首屏降低了 16%。
无线旺铺 - SPA 改造 - Tab 数据预载
策略
无线旺铺 Tab 预载: 在首页停留 1.5s 后开始预载其他后续 tab 数据加快用户 tab 切换时的体验。
成果
发布后,无线 M 站首屏 20% 左右。
无线旺铺 - ProductTab 无限加载改造
背景
无线旺铺的 ProductTab 页面原本只展示部分产品的概览,展示更多需要跳转到新的页面,如下图:
跳转新页面会有用户流失,如果能在当前页面直接无限加载所有品的话理论上会有更好的用户体验。很好奇为什么以前没有做,咨询了之前无线旺铺的负责人,得到的原因是原本 weex 旺铺的首页因为是一个单页应用,本身内存占用量就很大了,如果 Prouduct 页面再去做无限加载可能会对端上内存造成更大的压力,有可能导致奔溃或者闪退,所以当时采用的是新开页面去承载。
后来旺铺做了 h5 改造,不再采用 weex 的技术体系,同时业务上也有在当前页面无线加载产品的诉求。所以做了 ProductTab 的无线加载改造。
方案
由于依然担心无限加载带来的内存压力问题,所以在项目上线过程中做了跨度为一个月的实验,同时技术上做了一些相关保障。
成果
最终 abtest 对比 ,ab_rate 相对提升了 1.288%。
总结
店铺从开始做体验优化时的 PC 和无线相对来说都不够极致的情况优化到现在,PC 首屏是优化前的 60%,无线是优化前的 37.5%, 提升巨大。
过程中也产出了快速接入流式渲染的方案。而且在业务数据上包括访问深度、停留时长以及转化也取得了不错的成果。
感谢过程中一起合作的同事以及老板的支持。
Footnotes
由三方生态设计师开发的模板 ↩