专栏首页前端桃园京东购物小程序购物车性能优化实践

京东购物小程序购物车性能优化实践

前言

作为京东购物小程序黄金流程之一,购物车汇聚了大量的营销玩法,页面内容丰富多样。页面节点树庞大、千变万化的特点,导致性能问题比较突出且优化难度较大。在纯技术角度的性能优化达到瓶颈之后,我们开始尝试基于业务进行性能优化。

Part 1 扫雷篇-分析性能问题

优化要点

  • 首屏时间:指用户打开页面到看到第一屏主要内容的时间
  • 渲染时间:指数据首次渲染或引起页面结构变化的渲染所花费的时间
  • 请求耗时:请求耗时越长,用户等待的时间越长
  • CPU 利用率:CPU 利用率达到饱和时容易导致页面白屏或闪退
  • 网络请求数:短时间发起太多请求会触发小程序并行请求的数量限制

分析工具

1、Performance monitor

它是小程序开发工具内置的一个可视化监控工具,能够在 OS 级别上实时记录系统资源的使用情况。

借助这个工具,可以监控 cpu 和内存占用量和波动情况,快速定位引起页面卡顿、机器发烫的模块,进而进行优化

Performances monitor

2、测试机

小程序性能分析工具较少,且开发工具的运行效果和真机差异较大,应尽量使用真机定位性能问题。

通常,我们使用 oppo r11 和 iphone 6s plus 等低档机排查性能问题,再分别挑选用户数占比较大的低、中、高档机器检验优化效果

3、监控系统

前端进行测速数据采集和上报,再通过监控系统分析页面的各项指标健康度,了解页面加载耗时情况,对性能优化有较大的参考价值。

监控系统

项目上线前,可以通过测试机检验优化效果,但测试数据毕竟不能反映用户实际情况。因此,像购物车这种,展示内容与用户强相关的页面,非常有必要使用监控系统辅助分析。

ps: 下文各项测速数据均来自监控系统。

分析过程

1、购物车业务分析

  • 商品信息复杂度高。一个商品需要展示的信息量,可能占据手机屏幕的四分之一、二分之一、一整屏,甚至超出一屏。
  • 商品归堆方式复杂。购物车商品普遍只需按照店铺归堆,但是京东的购物车在店铺归堆的基础上,还要按照促销活动归堆。

2、测速数据分析

购物车数据复杂度高,因此我们重点观察了商品总数与首屏渲染耗时的关系:

  • 首屏渲染耗时受商品总数影响。怀疑与分屏策略相关,分屏渲染的粒度应细化到商品层级,而非促销层级,从而降低促销活动对首屏渲染耗时的影响
  • 商品数量大于 10,首屏渲染耗时明显上涨。通过多次实验发现,首屏渲染的商品数<=5 时,首屏渲染耗时与空车渲染耗时非常接近,在同一梯度;当商品数>5 时,首屏渲染耗时会上升一个梯度。

结论:需进一步优化渲染策略,尽可能减少首屏渲染的商品数。

Part 2 实践篇-性能优化历程

自动分页渲染

背景

早期,为缩短白屏时间,购物车使用了分屏渲染技术,把数据分为首屏和非首屏两部分,首屏渲染完成后再渲染非首屏数据。 分屏渲染最大问题在于,一旦非首屏数据量过大,渲染耗时会很长,让用户等待很长时间,最糟的情况可能引起页面假死,严重影响用户体验。 随着业务增长,这个问题带来的影响已经越来越明显,因此我们开始考虑改用分页技术

1、技术选型

难点:

  • 业务复杂。短期内无法实现分页请求数据,只能实现纯前端分页
  • 数据量大。每个商品不仅包含主品的各项信息,还可能附加与商品结构类似的赠品、换购商品等
  • 商品列表顺序动态改变。例如修改商品促销,该商品可能由列表第一项变成最后一项,操作完成后还要定位到该商品

技术选型

综合考虑各种业务场景和各项分页技术的特点,最终决定采用自动分页渲染技术。

2、基本思想

  • 一次性请求全部数据
  • 将数据分成若干页,每次只渲染一页
  • 上一页渲染完成后,自动循环渲染下一页

3、循环渲染实现方案对比

  • 通过 setData 递归。setData 的回调函数触发时立刻渲染下一页。缺点是会导致 UI 线程一直忙碌,用户操作响应变慢。
  • 利用 setTimeout。setData 回调函数触发时,用 setTimeout 延迟一段时间再渲染下一页。缺点是执行时间不可控。
  • 利用时间分片。通过 requestAnimationFrame(简称 raf)实现。调用 raf 之后,浏览器在准备渲染下一帧前会调用你传给 raf 的回调函数。按照帧率为 60fps 来计算,每一帧的间隔在 16.6ms 左右。

通过多次实验对比,最终我们选择时间分片模式。

4、示例

Demo: https://developers.weixin.qq.com/s/XJEDb3mP7Kex

原理: 用 raf 代替定时器,每次 setData 完成后,延迟 16.6ms 左右,再渲染下一页

实现思路:每次 setData 时触发 wxs 事件监听器,在 wxs 事件处理函数中调用 raf,raf 回调执行时调用逻辑层函数渲染下一页

流程图如下:

raf demo 流程图

5、效果对比

下面是使用分屏渲染(左图)与自动分页渲染(右图)的效果图。可操作时间缩短 50%+

分屏渲染与自动分页渲染对比

移除 scroll-view

scroll-view 组件是一种滚动视图容器,它提供了一个名为 scroll-into-view 的功能,可以使视图滚动到指定元素,为方便描述,下文简称定位功能。

1、背景

购物车很多场景用到定位功能,小程序不支持 DOM 操作,使我们不得不使用 scroll-view 这个大型组件。

但是,scroll-view 组件存在较多性能问题:

  • 与 view 组件相比,scroll-view 占用内存和 cpu 资源更多。可通过 Performance monitor 观察 cpu、内存使用情况
  • 滚动体验比页面自带滚动效果差
  • 在一些低端机或页面节点总数较多的情况下,scroll-view 组件很容易引起掉帧、白屏

节点复杂度越大,总节点数越多,scroll-view 暴露的性能问题越明显。

为了优化滚动体验,减轻 cpu 和内存压力,我们移除了这个组件的引用。

2、难点

想要移除 scroll-view,必须找到定位功能的替代方案。在微信基础库版本 2.7.3 以上版本,可使用 wx.pageScrollTo 的定位到锚点功能,对于 2.7.3 以下版本,需要自己编码实现。

示例代码:

_pageScrollTo ({ partten }) {
    if (this.createSelectorQuery && wx.pageScrollTo) {
        const query = this.createSelectorQuery()
        query.selectViewport().scrollOffset()
        query.select(partten).fields({ rect: true })
        query.exec(function (res) {
            if(res) {
                const windowHeight = (wx.getSystemInfoSync() || {}).windowHeight || 619const offsetY = windowHeight * 0.35// 目标位置距离窗口上边界的偏移量const _scrollTop = res[0].scrollTop // 滚动条竖直滚动位置const _top = res[1].top // 目标节点上边界坐标const scrollTop = _scrollTop + _top - offsetY
                wx.pageScrollTo({ scrollTop, duration: 0 })
            }
        })
    }
}

建议关闭 wx.pageScrollTo 的滚动动画。在一些低端机器上,滚动过程中页面部分区域会白屏,且不会自动恢复。另外,基础库 2.6.4 以下版本,滚动过程中 fixed 元素会闪烁。

3、效果对比

移除 scroll-view 之后,列表滚动过程中几乎不会白屏。下面是 scoll-view 移除前后效果图。

滚动优化前后对比

数据预加载

指提前请求数据,打开页面后使用预请求的数据渲染

1、分类:

  • 跳转时预加载:即将发生页面跳转时请求数据
  • 预判预加载:预测用户从页面 A 进入页面 B 可能性大,在页 A 内主动请求页 B 的数据

2、跳转时预加载

跳转时预加载

目的:缩短白屏时间。从发生跳转动作,到目标页面 onLoad 触发,中间有一段时间间隔,利用这段空闲时间提前加载数据,可缩短白屏时间

原理:触发跳转操作后,在页面发生跳转前(即调用 wx.navigateTo 之前),调用目标页面的预加载处理函数,函数触发时开始发送请求。进入目标页面后,使用提前加载好的数据渲染页面

3、预判预加载

预判预加载

目的:提前加载目标页面请求,大幅缩短目标页面白屏时间。

原理:预测用户从页面 A 进入页面 B 的可能性较大,在页面 A 内主动调用页面 B 的预加载处理函数,提前加载请求。进入页面 B 后,使用预加载数据渲染首屏

缺点:

  • 对数据时效性要求较高的场景不适用
  • 预加载后,用户不一定会打开目标页面 B,可能造成资源浪费

4、效果对比

与页面 onLoad 执行才加载数据(即无预加载)相比,预判预加载首屏耗时缩短 40%,跳转时预加载首屏耗时缩短 27%。下面是无预加载(左图)与预判预加载(右图)效果图。

利用缓存

缓存是一种备受青睐的性能优化方法。不仅可以减少请求数,降低弱网场景空窗率,合理利用缓存甚至能使首屏耗时缩短至少 30%以上。

它适用于数据稳定性强,时效性要求低的场景,如图片、字体、配置文件等。

像购物车这种,商品列表变化比较频繁的场景能否使用缓存?

答案是肯定。

1、实现方法

实际上,购物车的数据结构比较复杂,数据量大,为避免引起性能问题,只会 setData 关键数据。但为了方便查找数据和逻辑运算,还维护了一份缓存数据,每次请求购物车接口都更新缓存。再次打开购物车页时,只要满足一定条件,就可以直接使用这份缓存数据渲染页面。

2、效果对比

缓存模式首屏时间比常规模式(页面 onLoad 时加载数据)快 44%,比预加载模式快 23%。下面是预加载模式(左图)与缓存模式(右图)效果图。

逻辑后移

指将部分前端业务逻辑移至后台。

1、意义

  • 减少前端代码量。对于大型小程序项目至关重要,不仅因为小程序代码包大小有限,若代码包过大,会直接影响小程序整体性能和页面切换速度
  • 利于多端统一。复杂业务由后台统一实现,前端仅做简单处理和展示,有效避免因各端开发对业务理解的差异,导致各端页面实际展现效果不一致
  • 降低人力成本。业务逻辑统一在后台实现,避免各前端重复开发。测试童鞋可重点关注后台逻辑准确性,无需分别对各端进行详细的业务逻辑验证
  • 增强项目可控性。与 h5 不同,小程序发版难度大、周期长、线上多版本并存,一旦出现紧急需求或业务问题,前端很难快速解决

终极目标:类似需求前端只开发一次,后期迭代由后端开发完成后直接上线。

2、改造过程

逻辑后移过程

参考规范:

  • 仅用于一处文案展示。仅用于在固定位置展示文案,没有其他模块用到相关数据,建议后台直接下发文案
  • 用于标识。用于标识某种类型、状态,建议将相同类型的字段合并为一个字段。
  • 用于复合场景。既要展示文案,又要用于其他业务逻辑计算。先看看相关模块是否可以一起改造,如若不能,建议后台下发关键数据,前端做简单文案拼接处理

总结

实际上,购物车页的数据加载和渲染采用了多策略组合方式,根据场景和当前机器环境的特点动态选择数据加载和渲染策略,目的是在保障用户刚需的同时为更多用户提供更极致的体验。例如某些场景使用缓存模式+自动分页渲染,而在一些兜底场景使用的是常规模式+触底渲染等等。

本文重点在于介绍购物车页性能优化的过程和思考,没有过多深入技术实现细节,希望大家阅读后能有一些新的想法。

参考文章

[1] 页面渲染机制与性能优化:https://segmentfault.com/a/1190000016458627

[2] 从浏览器多进程到 JS 单线程,JS 运行机制最全面的一次梳理:https://cloud.tencent.com/developer/article/1547827

[3] 高性能渲染十万条数据(时间分片):https://cloud.tencent.com/developer/article/1509390

[4] 前端性能优化之缓存利用:https://www.haorooms.com/post/cache_huancunliyong

[5] 再谈前端性能优化:https://www.cnblogs.com/cherryblossom/p/7866324.html

[6] 前端性能优化(超详细):https://www.jianshu.com/p/30c42b04623c

[7] https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame

[8] https://www.w3cplus.com/javascript/requestAnimationFrame.html

本文分享自微信公众号 - 前端桃园(fetaoyuan)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Chrome Dev Summit 2019,你需要知道关于前端性能的内容都在这里

    11月12日,一年一度的Chrome Developer Summit举行,会议主要会聚焦于前端相关的主题,例如PWA、用户体验、性能、安全与隐私等等。在两天的...

    桃翁
  • 谈谈Web应用中的图片优化技巧及反思

    这篇文章,我们将一起探讨,web应用中能对图片进行什么样的优化,以及反思一些“负优化”手段

    桃翁
  • 2019 TWeb 腾讯前端技术大会精彩回顾

    讲师先是介绍了 flutter, 接着讲了腾讯企鹅辅导上的实践, 包括了安卓, iOS 和 Pad 上的原生应用如何嵌入 flutter 以及原生页面与 flu...

    桃翁
  • 京东购物小程序购物车性能优化实践

    它是小程序开发工具内置的一个可视化监控工具,能够在 OS 级别上实时记录系统资源的使用情况。

    WecTeam
  • 京东微信购物首页性能优化实践

    一般来说产品是按以下方式进行迭代的,我认为循环的起点应该是「收集用户反馈」,我们对页面的优化依据和目标一个重要来源就是用户的反馈,因此说网页优化我们先从网页监控...

    WecTeam
  • 京东微信购物首页性能优化实践

    一般来说产品是按以下方式进行迭代的,我认为循环的起点应该是「收集用户反馈」,我们对页面的优化依据和目标一个重要来源就是用户的反馈,因此说网页优化我们先从网页监控...

    前端劝退师
  • 使用预渲染提升SPA应用体验

    在目前的前端领域,单页web应用(SPA)已经有了比较高的占有率,比较主流的web框架React、Angular、Vue几乎已经统治了前端市场。

    Javanx
  • 前端基础-Node.js核心模块的使用

    要求2:向 hzw.json 文件中添加一条数据 {id:'4',names:'罗宾',sex:'女',img:''} ;

    cwl_java
  • CreatorPrimer|Creator 2.x渲染初探

    Shawn之前只是从感性的一面了Cocos Creator 2.0在性能更好,增加部分3D效果的支持,同时API有一些变化。随着对Cocos Creator 2...

    张晓衡
  • 【Visual Force学习】使用简单的变量和公式

    Visualforce 页面能够显示从数据库或 Web 服务中得到的数据, 根据登录人以及浏览页面的用户的不同数据也可随之变化的等。

    臭豆腐

扫码关注云+社区

领取腾讯云代金券