Weex 详解:灵活的移动端高性能动态化方案

阅读数:9720 2016 年 4 月 29 日

在 2016 年 4 月份的 QCon 上,阿里巴巴资深总监,淘宝移动平台及新业务事业部、阿里百川负责人庄 卓然(花名南天)宣布阿里移动端跨平台开发框架 Weex 开始内测,并将于 6 月份开源。在 QCon 的第二天,阿里技术专家徐凯(花名鬼道)和阿里前端开发专 家赵锦江(花名勾股)向参会者做了《Weex——灵活的移动端高性能动态化方案》的演讲,对这一技术方案进行了详细的剖析。

以下为演讲内容的整理:

昨天南天宣布 Weex 启动开源内测,截至到今天中午,我们统计申请内测用户突破 1400 人,大家的热烈程度远远超过我们的设想,非常感谢大家支持。

在我们对移动开发最佳实践的思考中,我们认为移动开发的未来是更平衡的方案,一定是性能和动态性兼得。第二个,它一定是开放互联的,PC 端一直也是这样的, 也是非常好的状态。我们觉得移动互联网将来肯定也是基于更大众化的技术体系,没有平台之间的隔阂,简单直接易用,这是我们最希望看到的。基于这些设想,我 们有了 Weex 方案。

Weex 是从去年双十一的时候第一次在我们正式产品中使用,承载了双十一主会场的工作。有人会问,Weex 是不是除了做主会场别的地方就比较吃力呢?从去年双十一到现在,包括我们自己的尝试和阿里内网做开源内测活动,大家也 贡献了很多内容,包括昨天 Keynote 演示的僵尸动画,扫雷、计算器都有,各种丰富场景的东西都可以通过 Weex 做出来,不仅仅是做主会场的技术方案。

对移动端开发模型的理解

我们谈谈 Weex 团队对移动端开发模型的理解。

今天绝大多数移动端 APP 是这样一个最佳实践,首先把移动端所有界面拆分成各个 page,中间有一个路由的控制逻辑。同时我们需要移动端各种各样丰富的能力,通过 API 的形式提供给开发者。这是我们认为一个比较理想的开发模型。

从开发模型来讲,我们比较倾向于通过标准化的一些东西,包括 HTML、CSS、JS 这些前端非常快速易用好学的语法作为一个开发体验,提供给开发者。这里强 调一下,我们语法设计尊重了 Web 标准,包括核心源代码都是从 Vue.js——一个非常优秀的 MVVM 前端框架来的。我们的开源内测同步在海外通过 Vue.js 的 Twitter 等途径进行宣传,得到了非常热烈的反响,短短一天时间内,老外们的关注度和热情也是出乎我们的意料。有些人非常兴奋和激动, 想知道什么时候看到源代码,和我们联系,我们觉得也是值得高兴的一件事情。

Weex 的组件化与 DSL 语法

Weex 编写的页面天然的支持组件化,首先,我们的界面可以是一个组件化的,把一个复杂界面分成每个组件,刚才演示的都是简单的组件,每个组件都可以看成是一段 template,style,script,放到模型里,对应到界面的结构,样式细节,行为定义。在 view 里面我们倾向于把数据和视图当中需要展示和 需要有动态变化的部分做一个数据绑定,绑定之后我如果想更改界面的话,通过改变数据就可以做到。这是从 model 到 view 非常顺畅的控制逻辑和代码方 式,这也算是 Weex 上层语法设计的基础。如果大家做的界面比较复杂,可能有更多细节,或者做更多分解,我们需要从整体上对整个界面以模块为单位作为拆 解,对每个模块做定义,如果界面足够复杂,可以先拆成组件,再把每个组件具体内容进行定义。定义方式就是通过结构、样式和行为角度对它进行定义,通过有机 方式把这些组件结合起来,完成页面开发。

关键语法简述。首先看看 template,大概有几个元素。它首先是 virtual DOM tree 展示,包括事件绑定,组件跟组件之间还可以嵌套,还可以有子元素,这是整体 template 结构。再往下是 style,一方面可以把共用样式抽样 出来,另一方面可以让 template 结构更加清晰,不至于陷入到整个具体样式描述当中去。我们在这边会做一些收敛,我们只支持了单个 class 的 selector 写法,主要从性能角度考虑。传统的 css 可以理解为是一个 N 对 N 的数据库,匹配过程非常复杂,性能也得不到非常好的保证,我们为了保证性 能,我们把 selector 约定在单个 class,性能可以保证。

另外,我们的样式天然默认的就是 scoped,大家可以放心定义各种各样的 classname,不担心和其他组件相冲突,全局冲突是 CSS“七宗罪”中的一个,其实这也是我们非常重视的,所以在实践上我们把它做到了 scoped。

其他的,可以做一些展示的控制,比如 if、repeat 刚才演示中也提到了。刚才没有演示到的这个很特殊,append,在性能不是那么好的 Android 下,界面加载过程用户是可感知的,不是一瞬间做到的。我们加这个值可以让你精细化展示界面的颗粒度,如果上层做了 append 等于 tree 的话,里面一系 列东西会做一次性的加载,这是从性能优化角度做的特殊设计。再是 id,我们可以通过在这里面写 id 拿到这个值,把它当作参数传给 API 做处理。

我们现在已经支持的组件,除了刚才演示的 div 空白容器、图片、文本之外,我们还支持 slider:一个性能比较好的滑块组件,还有 list,性能上自动做内存管理和资源管理的组件,把性能和帧率各方面都做的比较好。还有 input,输入框我们是最近才做的,也是刚支持的,可能还有试验性阶段的小东西。在这 个范围之外,业务方可在上层横向扩展,稍后会有具体介绍。这是 template 和 style 部分的介绍。

样式部分我们支持 flexbox,非常灵活通用便捷的布局方式,有了 flexbox,只要你的界面可以拆分成豆腐块的,都可以用 flexbox 来做,同时我 们还做了 fixed 和 sticky 这两个特殊的布局,sticky 意思是如果有一个列表分类,比如联系人 ABC 字母滚到屏幕外,会停在最上面,那个效果就 是 sticky,当你划走它就会跟着走,在各种手机应用当中是一个比较常见的东西。同样,更多的样式每个组件也可以自己定制,非常灵活。

从事件角度,click 是基础事件,change 在表单的值改变和滑快的第几帧改变时都会有,同时我们加入了 appear 和 disappear,当我通过任意操作进入屏幕区域内,会触发 appear 事件,出来以后会触发 disappear 事件,非常适合用在一些 lazyload 之类的逻辑场景。这些事件也可以在各自组件中做横向扩展。

Script 部分刚才例子也提到了,主要是 viewmodel 设 计,最主要的是 data 和 methods,值修改之后相应数据绑定的值也会发生改变。除此之外我们提供了生命周期的方法,创建的时候,数据监听完成的时候,渲染完成的时候,你只要把这三个方法同样写在 data 和 methods 下面。还有原生 API,刚才演示的时候也出现了,这里不多介绍。

组件化搭建很复杂,通过定义子组件,比如我上面有一个 foo 组件,通过 foo 标签就可以把 foo 嵌入到别的组件中来,数据传递的话就可以在 foo 组件中写 a 和 b,foo 元素就可以通过这种方式传递给子元素,然后进行处理。再是组件中间的通信,也是事件机制,每个组件可以通过$on$off,对自定义事件进行监听和解绑定,想触发事件也有三个方法:$emit只传给自己、$dispatch向上冒泡给所有父组件、$broadcast广播给所有子组件,这些设计和 Vue 非常相近的,做到组件之间的通信。

除了刚才介绍的这些特性,我们未来还会提供更丰富的、相信也是开发者需要的、平时开发中会碰到的场景,更丰富的表单,还有更好的动画展示,包括复杂手势场 景。在这方面,无论是性能还是开发体验、最终效果都能够非常好,这是团队正在努力在未来提供给大家的。同时我们希望收集和分享更多相关素材,做出更多工 具,提供更好的开发者体验给大家。

更多的内容,包括刚才演示的 Playground App 都可以通过我们的官网找到,现在处在开源内测阶段,大家提交申请,通过以后可以访问仓库,了解更具体的内容。

Weex 的工作原理

这张图大家都不陌生,前面也提到了,勾股说的主要是 DSL 这层。再往下到了 Virtual DOM 和 Render 层;H5,我刻意把它用不一样的颜色标出来,想让大家知道我们设计之初就考虑到希望在三端上能够展现,所以这个地方稍微加亮一些。

我 们把刚才这张图再稍微展开一下,最上面是我们的 DSL,我们一般叫 Weex 文件,通过 transformer 这层,部署到 Server,服务端就完成了。 大家不用担心我们的转换是不是有性能问题,因为这在服务端就已经完成。到了客户端,第一层是我们的 JS-Framework,最后到 RenderRengine,再往下看,左边是我们的 DSL 文件,右边是转换出来的 jsbundle,在 DSL 中的 template 会将我们的类型和子节 点都表示出来,将 classList 转化成基本语法约定,包括自变量的转换。最后是脚本,脚本基本上是直译过来。输入是 Virtual DOM 输出是 native 或者 H5 view,还原成内存中的树型数据结构,再创建 view,把事件绑定在 view 上,把 view 基本属性设上去。虚线是在 native 上经历一个过程,在 H5 上相当于把这个事情交给 webkit LayoutEngine 去做,把所有元素尺寸和位置重新调整。整个这张图基本上讲清楚了 Weex Render 流程,我们会分三个线程,不同的线程负责不同的事情,让 JS 线程优先保障我们的流畅性,未来我们会有更多的技术文档,比较细节的放出来。

Weex 的性能、扩展以及可用性

下面是在整个 Weex 架构上比较关键的点,这些可能是我们目前关注度最高的,包括性能、扩展以及可用性。

首先是性能,我们内部有这样一个压测页面,我们同学把 benchmark 放在 Playground,大家如果下载是能够看到的。在我们内部做压测的时候调到三千个节点,大概 10 屏,一屏有三个卡片,一个卡片有 100 个节点左右。我们看一下数据,第一个性能对比是我们的加载时间,同样一个页面 1300、 1600,也不算特别大,20% 左右,帧率大概差开一帧,scroller 差不多,内存这块会好一点,因为我们这边用了 recycle view,会好一些。再往下是 CPU,静默 CPU 消耗,还有运行过程中 CPU 的峰值。静默 CPU 接近 0 点几,我们不做 16 毫秒的轮循,如果做 16 毫秒轮循 CPU 会更高一些。

这是一个真实业务数据,3 月份页面上线之后我们看了一下,这张页面是一个活动,3 月份新风尚的活动,这个活动页面没有用 List,没有特别做内存对比,这两 个设备定义为低端机,帧率差压比较明显,无论是数据还是实际中的体验,流畅性大概是这样的差距。我们说性能往往都会提到帧率、加载时间,但往往会忽略一个 事情,Native UI 开发中通常没有 JS 资源在服务端加载,Weex 以及类似动态化方案有一个副作用,我们有一个 JS 必须从服务端下载,我们把 JS 内置到客户端里,免除下 载的问题,这里涉及到一整套的策略。我们内部有一套机制,之后会把这套机制作为独立的技术产品开放出来。

下一个是我们的兼容性。兼容性不只是对 Weex,对偏内核型项目都会有这个问题,举一个 Weex 例子,第一排是我们的业务代码,再往下看,上面两次变迁,一直到客户端,整个场景会变成 N 的立方。举一个具体数字,我的业务代码改了三个版本,JS Bundle 三个版本,weex 三个版本,最后会有三的立方 27 个场景。兼容性是我们一直重视的问题。我们做了几件事情,首先单测保证,包括 JS 和 H5 的单测,保证最最基础的 UT 本身带来的含义。第二个是 JS 单测环境,我们一般会将测试跑在模拟环境下,但和真机还是有差异。再往下是自动化工作,这块工作细分也可以分成两块:一块是我们针对截图比对,比如我们同学会说我们设置了很多各种各样细节属性,怎么说你渲染出来的就是你实际想要的,通过 API 级别的效果不是很好。所以这种我们会通过截图,将最终产生的结果和我们意料中的结果进行图形比对,比较老的成熟的内核上面做的比较成熟,也会有一些借鉴。另外是 layout results,相当一部分,包括 model,包括其他的布局类的,其实我们完全可以通过一个元素,最终它的宽高,左上角的点,通过基本的信息,让它完成测试的过程。所以我们经过这两块工作,一旦成熟,我们会尽快放到上面。

再就是扩展性,我们先回顾一下这张图,前面也有提到,目前 Weex 给大家直观的感觉是可以用 Weex 写很多页面,有一个路由机制,内部叫导航,帮助你将页面进行串连,我们提供很多 features,由这样的形式构成 Weex 大家所看到的一个结构。细分来看,如果你扩展一个 component,特定的一些方法,使用 anotation 标识输入输出参数。这是一个 module,在 DSL 上看到的是 API,底层就是 module。如果扩展 module 也是一样的,这是很简单的跳转,基本信息带上去,实现业务功能,一个 module 就完成了,很简单。

这是路由,整个路由在 APP Framework 中只是一个环节,所以接下来的规划还是有许多东西需要做的,单独看 components 这块,包括前面两个,再往下是 lifecycle 考虑,再是动画跟手势,这块我们觉得都是最基础的东西。再往下是全局的多个 Weex 容器之间通信机制,数据存储、网络,包括下面涉及到的一堆传感器,包括基础的 FS,还有偏业务类的东西,整个东西都有同步在做,但现在的工作集中是在这块。回到刚才的老问题,如果我们开放一个 module,上层提供 API,中间提供 adapter,如果提供自由实现就将默认的覆盖掉,比如手淘里面(TBxxx),把默认页面覆盖掉 (WXxxx),对大家来说在已有能力增强,或者是增加新的组件,或者是新的 module 上,目前已经达到这个状态。

最后是我们的可用性,前面说的比较多,这块秀几张图就好了。这是我们的工具链,红色代表完成的,黄色是五月份、六月份就会完成,在六月正式开源之前。剩下一 些东西是我们内部正在讨论以及会安排时间逐步完成的东西,这些都是工具。我们首先给大家提供一个 playground,可以扫码,也有自带的 examples。第二个是调试工具,chrome dev tools 常见的功能里面都有。再是脚手架,大家如果只是玩玩的话用 playground 就可以了,如果自己做一个独立的 APP,你要用 Weex 做的话我 们也会给你提供脚手架。我们的规划中独立项目,不是最终的名字,最终会有一个类似 APPHub 的工具。包括信息察看,在界面上展示结构。这是刚才提到的例子,这个是 playground 里面的,虽然截图是 iphone 效果和 android 是完全一样的,已经用 Weex 现有功能做出比较好玩的组件库。然后是我们的文档,包括项目 guide、reference、toolchain,细节我不多说了,大家可以去看看。

Weex 的集成方式

目前 Weex 有三种集成方式:

  1. 全页模式

    o 目前支持单页使用或整个 app 使用 weex 开发(还不完善,需要开发 router 和生命周期管理)这是主推的模式,可以类比 RN。
  2. Native Component 模式

    o 把 weex 当作一个 iOS/Android 组件来使用,类比 ImageView。这类需求遍布手淘主链路,如首页、主搜结果、交易组件化等,和业务同学沟 通下来这类 Native 页面主体已经很稳定,但是局部动态化需求旺盛导致频繁发版,解决这类问题也是 Weex 的重点。

    o 这也涉及到如何让 Native 同学快速上手“准 Web”开发,有意思的话题,大家多给些建议。
  3. H5 Component 模式

    o 在 H5 种使用 Weex,类比 WVC。一些较复杂或特殊的 H5 页面短期内无法完全转为 Weex 全页模式(或 RN),比如猫超、互动类页面、一些复杂频道页 等。针对这个痛点我发起过 WVC 项目,并在实际业务中验证了这样的想法:在现有的 H5 页面上做微调,引入 Native 解决长列表内存暴增、滚动不流畅、动 画 / 手势体验差等问题。

    o WVC 将会融入到 Weex 中,成为 Weex 的 H5 Components 模式。

    这 3 种模式几乎涵盖了淘系业务上的动态化需求(针对 Native)或体验提升需求(针对 H5)。更有趣的是这 3 种模式的技术基础是一致的,这非常重要,意 味着:业务方可以使用 Native 或 H5 Component 模式 解决实际的业务痛点,同时平滑过渡到 Weex 全页模式。期待 Weex 成长壮大到 AppFramework 的那天。

最后是我们过去双十一到现在大概五个多月时间做的一些事情。首先我们做了原型版本,再将原型版本独立出独立客户端可使用的 SDK,扩展样式和布局,将基础的 component 做了扩展,这两个月集中在工具,还有 open source 上的工作,最后是六月底就会开放出来。

收藏

评论

微博

发表评论

注册/登录 InfoQ 发表评论