隐风 阿里巴巴技术专家
本文是 2018 年 Weex Conf 中议题 Weex 技术演进的内容文档整理, 主要给大家介绍过去一年里 Weex 业务规模不断扩大, 业务复杂度不断上升, 给 Weex 带来了哪些技术挑战, 以及 Weex 在技术架构和设计上做了哪些升级来应对这些挑战
业务规模
2017 年, Weex 的业务规模呈现了爆发式的增长, 不但在比较擅长的会场业务弥补了最后 1% 的页面, 也就是一些富交互富视觉的页面, 让店铺承接页也全部 Weex 化, 同时更重要的是支持了很多常态化的业务和产品
技术挑战
业务规模的不断扩大, 业务复杂度的不断上升, 给 Weex 的技术到底带来了什么样的挑战?
交互体验问题, 我们在 2016 年做的更多的还是纯展示的活动页面, 而 2017 年要支持的这些常态化的业务和产品, 很多原来就是 native 的, 对于动画手势等交互体验要求比较高, 一些 native 非常常见的交互效果, 在 Weex 上实现起来挑战非常大
内存压力, 越来越多的业务 Weex 化之后, 用户整个点击路径都变成了 Weex, 举个例子, 2017 年的双 11 预售期, 当我们打开手淘, 点击狂欢城 H5, 再点店铺承接页 Weex, 点主会场 Weex, 点分会场还是 Weex, 然后分会场分会场不断地逛, 链路中每个页面都非常复杂, 其中大部分页面是很长的列表页, 包含了大量的楼层视频动效等复杂组件, 整体内存占用非常高, 这就导致整个 APP 的 crash 率上升非常明显 2017 年我们升级了列表渲染架构, 针对内存问题重点优化了一次
性能稳定性, 如何把 V8 引擎升级到 JSC 来提升 JS 执行性能, 如何能让 JSC 在独立进程运行而不影响主进程稳定性, 如何解决 JS 上下文全局污染问题, 如何替换 Yoga 以解决 License 问题而又不影响布局性能等等
WeexCore, 如何在架构上实现跨平台和高性能, 将一些通用的逻辑下沉到 C++
演进过程
纵观 Weex 一开始诞生到现在, 它的技术演进过程可以总结为从具象到抽象不断探索的过程
特殊组件期: 最开始 Weex 亮相的时候是 2015 年双 11, 当时只支持了一个会场页面那时候我们做了非常多的特殊组件, 比如 tabbar 组件, 倒计时就做一个 countdown 组件, 跑马灯就做一个 marquee 组件当时的 Weex 可以理解为是一个非常具象的框架, 由一大堆特殊组件拼接而成, 最后达到了动态化的目的
偏展示期: 到了 2016 年双 11, 我们要支持整个双 11 所有会场, 几千张页面, 继续使用 2015 年的方式肯定是不行的于是我们做了很多偏展示的基础样式, 比如支持 text 的基础样式, div 的基础样式, 最基本的动画模块, 存储等一些基础且细粒度的模块等, 以及一个相对来说比较高性能的列表组件
富交互期: 到了 2017 年中, 我们要支持一些固定的产品, 一些核心业务, 偏展示又满足不了需求了, 于是我们重点去打富交互, 支持富文本手势布局动画以及一些通用的交互组件
国际化: 到了 1112 月时又来了一波挑战, 我们要支持 Lazada 等国际化的 APP, 于是整体做了一次国际化, 比如从右到左的文字排布, 比如无障碍多语言多端差异化等
标准化: 到了 2018 年, 我们要做的是标准化, 尽可能抹平跨端在组件和架构上的不一致性, 遵循标准并推进标准, 让 Weex 变得更加通用
交互体验升级
什么是好的交互体验?
我们做交互体验首先要问的一个问题, 到底什么是好的交互体验? 2017 年之前, 我们会关注几个特性, 第一, 加载速度很快, 希望用户从点击页面到最后整个首屏渲染完, 在一秒内完成, 也就是我们经常说的秒开第二是滚动流畅, 用指标描述就是 60FPS 到了 2017 年要求就更高了, 当时交互同学给了我们一些例子, 看一下在他们眼中好的交互体验是什么样的
第一个例子, iOS 原生的 mail app, 随着左右滑动的手势, 两边的 view 可以灵活地做动画, 这个在业务上非常常见比如左滑删除, 右滑关注之类的功能
第二个例子, 有好货, 它是由多个 tab 组成, 每个 tab 都是很长的列表下面可以通过手势操作列表, 同时 tab 可以灵活的进行切换
最后还有一个微博的例子, 也就是我们经常所说的滚动视差体验, 随着列表下拉, 图片变大或者变模糊, 随着列表向上滚动, 图片以不同速率移动一句话概括这些例子对 Weex 交互体验的要求, 那就是随着用户的操作, Weex 页面需要持续又快速的进行响应
需要遵循哪些原则?
最大化利用 native: 我们做交互演进过程中, 一直在思考 Weex 最大的价值是什么我们认为, Weex 最大的优势和价值在于其 native 的能力, 我们要最大化利用 native 的特性, 而不是去模仿它怎么去理解? 举个例子, Native 的列表组件, 有着非常好的滚动流畅度内存控制能力 View 复用能力, 我们希望把它的所有能力都能透出给前端 JS, 而不是想着在 JS 侧重新再模仿一个长列表组件
从下至上: 首先竭力解决最底层的东西, 比如通信的消耗手势基本的动画文字的基本样式如果这些问题解决起来成本太高, 我们会暂时先往上, 封装一些通用的组件模块级的解决方案, 来解决大部分应用场景再往上就是封装业务组件, 把 native 的一些复杂的能力整块整块地丢给 JS
允许多端差异化: 允许 Android 遵循 MD 的设计规范, iOS 遵循 iOS 的一些设计规范, 一些复杂的交互和视觉, 运行 web 进行体验上的降级
如何解决通信时延的问题?
在交互体验升级过程中, 最大的挑战是 JS 和 native 的通信时延问题, 举刚才滚动视差的例子, 随着滚动, 每一帧 native 发起滚动事件, JS 监听做一些处理, 计算出要发生多少 translate 位移, 重新告诉 native,native 再做动画, 整个循环需要在一帧也就是 16 毫秒内完成现实情况下非常难做到, 大部分情况下这个流程下来至少都是 50 毫秒当时我们用这种方式也做了一个 Demo, 滚动视差的效果, 大家会发现抖动非常明显, 和持续快速响应的目标相去甚远
优化思路也很简单, 两个优化思路, 第一是减少通信次数, 希望一次通信把这些事情都搞定这里我们用了 Expression Binding 模块和 parallax 组件, 他们的原理类似, 就是将手势事件和 View 属性变更的关系提前绑定到 native, 当手势事件发生时, native 根据这个关系来选择输出, 不再需要发事件给 JS 及接受 JS 的指令
第二种思路是优化单次通信耗时我们发现通信大部分时间都花在序列化和反
序列化过程中我们自己发明了一个通信协议 wson, 用的是二进制传输协议, 序列化和反序列化效率提升很多同时因为通信时间的提升, 整个页面加载时间也有了一定的提升, 大概在 5% 左右
列表渲染架构升级
列表组件的前世今生
我们再来看一下另外一个重要的技术演进: 列表渲染架构的升级, 2015 年双 11 第一次亮相, 我们用的组件是 Scroller, 完成了从 DOM 树到 View 树的转换, 缺点是必须等 JS 解析完所有节点才会到 native, 一次会创建非常多的 View, 对一个长列表来说了经常需要 3-5 秒才能出来; 到了 2016 年年中时我们加了一个渐进式渲染的概念, node 和 tree 模式的灵活搭配, 最小粒度渲染, 解析完单个 DOM 后可立即显示但这个方案仍然也一些瓶颈, 它渲染了不可见区域, 内存占用比较高到了 2016 年的双 11, 我们使用了 List, 相对于 Scroller 好多了, 我们透传了 native 的 UITableView 和 RecycleView, 在内存的控制上提升明显, 比如只渲染可见区域, View 内存回收复用等
到了今年, list 也开始满足不了需求了, 刚才说了, 整个链路都是 Weex 长列表, 内存挑战很大而我们的 list 在内存占用上和 native 列表还是有不小的差距, 另外 list 还有一个比较大瓶颈就是无法做 view 的复用, 低端机滚动帧率较差
列表渲染架构解析
我们看一下老的 native 设计是什么样的, 这是偏底层的设计和架构解析假设一个业务有 M 个模板和 N 条数据, 我们现在做的就是在 JS 层做一个模板解析和数据绑定的工作, 这一层是 vue 做的 JSFM 会生成 N 个 Virtual DOM,Native 生成 N 个 native Component, 可视区域内有限个 native View
这样一个架构问题在哪里呢?
为什么做不到 View 结构的复用? 简单来说, 要做复用必须在 native 进行模板数据绑定, 现在在 JS 层已经绑定好了, native 拆不开, 没法做复用
为什么内存占用高? 因为 element 和 component 是重复生成的, 虽然他们只是一个个对象, 但当列表非常长, 无限滚动时, 它的内存仍然会呈上升的趋势
为什么帧率差? View 永远都是销毁掉重新创建, 主线耗时变高, 虽然中间也做了一些异步渲染的处理, 但还是没有达到理想的幀率.
什么是真正的复用, 其实就是类似于垃圾分类的过程, 数据就是垃圾, 不同种类的垃圾, 可回收垃圾有害垃圾厨房垃圾, Cell 模板就是垃圾桶, 我们只要把数据分门别类, 在 native 侧放到不同垃圾桶里就可以了回到刚才那张架构设计图, 我们需要做的, 也就是把数据绑定放到 native 看上去整个设计变化非常小, 但我们做了差不多三个月, 也找了尤雨溪配合我们在 Vue 上进行改造
最后是性能对比前段时间我们刚好研究了下 RN 的几个列表组件, 顺便做了一个性能对比, recycle-list 是我们新做的列表组件对比的案例是左边这张图, 有 60 屏数据, 在淘宝页面里不算特别长, 里面有 3000 + 节点看一下右边的性能数据报告, 总的来说, 最大优势就是内存, 在 iOS, 可以让这样一个长列表页面的内存占用控制在个位 MB, 同时因为通信的数据量变少了, 在加载时间 CPU 消耗上也有一定的提升
来源: https://yq.aliyun.com/articles/444881