【www.bbyears.com--.Net开发】
移动无线电商动态化
无线
首先要谈的是无线,或者说移动。手机和传统桌面端计算机有着非常大的不同,首先是随时随地,这样它就需要同时也可以拥有更多的传感器,比如镜头、体感、定位、蓝牙、NFC 等等,当然也包括多点触摸屏,人们的操作和交互方式也发生了很大的变化;其次它的屏幕尺寸和电脑完全不同,也由此产生了完全不同的阅读体验和阅读方式;再有就是能耗和稳定性变得非常非常关键,如何流畅的展示复杂界面和大数据,如何有效控制由于资源问题而导致的 crash,如何帮用户省电,如何长时间使用还不会发烫,都变成了很重要的课题。总而言之,一个输入,一个输出,一个性能。
还有一个潜在的问题,就是无线端很多形态大家都还在一起摸索之中,这其中有很高的“试错”成本,我们的产品经理甚至会在项目初期就主动坦承我们需要很多试错,需要快速迭代。所以在无线端探索性质的东西非常多,远远多过桌面端。
电商
电商这个话题其实挺大的,我只说一些从个人技术角度看到的电商:就是 1 什么地方都得能随时改,2 随时改。
从电商平台的角度,我们无时无刻需要为用户挑选和推荐更好更适合的选购信息,比如有新品上架了,有商品降价了,有促销活动了……它作为平台一定是自由的,信息是充分流通的,所以也必须是非常灵活实时的。
再有就是电商是和真金白银打交道的,这要求我们对技术方案的稳定性和准确性做非常严格的把控,对项目工程化有极高的要求,项目过程中的半点松懈都有可能对用户产生极大的影响。
动态化
今天的移动端,大家基本都是通过各式各样的 app 来完成自己的事情,而这些 app 都是 native 的,有必要的版本控制和发布节奏。浏览器的位置相对没有那么明显,更多的充当了一个“快速降级”的角色,即如果我们没有安装 X (类) 应用,那么当我需要使用 X 的时候,就用浏览器打开一个 X 的网站。
既然大家的主战场都是 native app 了,那基于上面我们对于无线和电商这两个关键词的分析和理解,如何在充分合理运用设备的输入输出、并且能够快速迭代、实时调整、还能把控性能和各方面风险,就变成了重中之重。
比如每年双十一当天,整个天猫和淘宝的各条产品线都处在高度“敏感”的状态,活动当天出现的任何一个状况都可能产生非常重大的影响,需要我们可以灵活调整;在平时卖家和买家产生的任何信息和数据,都希望可以最通畅的发布出来。尤其是卖家,如何“装修”好自己的店铺,“打扮”好自己的宝贝信息,“张罗”好自家的活动,都需要非常强的动态性做支撑。
移动端的 HTML5
先反观今天的桌面端,浏览器已经是绝对的主角了,我个人身为一个前端工程师算是亲身经历了浏览器从默默无闻到变成桌面端第一使用时长的全过程。而今天的手机则是 native app 的天下。我觉得原因有很多,比如体验、能耗、生态、入口等等,同时也是因为手机上的浏览器需要顺应很多颠覆式的变化并且在多个厂商之间寻求统一。
HTML5 其实在移动端一如既往的保有着它的跨平台和快速实现的优良特点,随着手机操作系统的不断升级,越来越多的 HTML5 新特性有机会在实际产品中运用起来。但也一直有很多局限性。
比如提供的功能有限――这个问题目前很多地方都在通过 Hybrid 方案来解决,把底层的能力或业务上的特殊需求封装成 JavaScript API 暴露给 webview,从而让 Web 页面可以通过 JavaScript 调到这些特性。相信任何团队在这条路上经过一段时间,都已经把问题解决得差不多了。
第二个问题是性能问题,今天在移动端,设备的性能,不论是从 CPU 还是内存还是电池都不能和桌面端同日而语,简单的界面用起来是没感觉的,一旦交互复杂或数据量达到一定程度,webview 的弊端还是比较明显的。
上述两点都是比较绝对的,而由于性能和能力缺失而引发的上层技术方案的倾斜则会产生更多深远的影响。比如二维码扫描与识别、省市区地理位置选择、包括一些复杂的动画设计,这些问题都不是“前端能不能做”的问题,而是做出来效果好不好的问题,或“卡不卡”的问题。
在桌面端,浏览器之所以能一统江湖,其实不只要归功于 W3C 和 HTML5,我们的浏览器还有一位“好朋友”:那就是 Adobe Flash Player Plugin,它虽然不是标准和开放的,但一直有着广泛的用户,也对浏览器的能力做了大范围的扩展,而性能也是 native 级别的,这恰恰解决了我们上面提到的几个问题――后期 Adobe 还提供过 AIR 平台,可以完全甩开 Web 开发桌面应用。而现在的移动端就没有这么好的“福利”了,迟迟没有人站出来充当“移动端的 flash”的角色。
看上去还不错的解决方案
我所在的团队一直在不断思考和探索无线电商动态化这个问题。在这个过程中,我们看到了很多不错的方案,也做了很多自己的尝试
传统 Hybrid:基于嵌入 native app 的 webview,通过对 API 的扩展达到解决问题的目的,这个已经流行好一阵子了,无需多提
为 Hybrid 引入 native 界面:我个人最早是在去年 QCon 北京听 集鹄 (大叔) 的分享,当时只有安卓版本,大概做法是以类似 plugin 的方式把一块 native 界面引入到 webview 当中,这样在整体还是 webview 的情况下,可以局部达到特殊的 native 效果。随后在今年的 Velocity 北京,也看到了一个类似的方案,这次 native 界面是“盖在 webview 上”的,并随着 webview 滚动条做相应滚动,以保持界面的一致效果。
WeApp:这是我们无线事业部2年前开启的一个项目,设计一套 JSON 格式,可以描述界面的结构和样式,然后各端实现对这套 JSON 格式的解析和渲染。这样我们只要每次请求不一样的 JSON 数据,就可以展示出不一样的界面效果,和之前的 Hybrid 方案不同的是,在 native app 里我们的展示效果是 100% native 的,并且我们还做了 HTML5 版本的“降级”渲染方案,用户在浏览器里同样有机会借助我们的渲染方案把整个界面展示出来。
React Native:这位老兄毫无疑问是今年全球最火热的移动技术方案之一,界面是 100% 的 native,彻底颠覆了移动应用的技术栈和开发体验。但最重要的是,它在运行时是用 JavaScript 进行控制的。这件事为我们打开了一个巨大的脑洞:一旦我可以在线发布和加载 JavaScript 文件,那也就意味着我具备了动态性的能力!于是乎出现了通过动态发布 React Native 代码达到动态性目的的技术方案。
最终
我们经过一系列的调研、思考和讨论之后,提出了一套完全针对无线电商动态化的技术方案。它同样:
致力于移动端
能够充分调度 native 的能力
能够充分解决或回避性能瓶颈
能够灵活扩展
能够多端统一
能够优雅“降级”到 HTML5
能够保持较低的开发成本
能够快速迭代
能够轻量实时发布
能够融入现有的 native 技术体系
能够工程化管理和监控
我们觉得它并不是那种第一眼看上去很伟大的东西,但是它能够帮我们解决几乎所有实际业务中遇到的问题,是个非常务实的家伙。
我们甚至觉得这个方案里面没有什么高深的东西,没有颠覆式的新科技,但我们非常针对性的关注细节,并且在实际业务当中产生的效果是绝对颠覆式的。
更令人振奋的是,这套技术方案在今年的双十一全球狂欢盛典中经受住了考验,我们成功的支持了主会场的前期研发和当天的发布即实时调整。这也让我们更有勇气和信心面对接下来的更多挑战!
我给它起了一个酷酷的新名字:Weex
Weex 技术方案介绍
先睹为快
有了正文介绍的背景情况,接下来我们分享一下 Weex 的方案设计和开发方式,当然在此之前,Weex 能够做出什么样效果的界面,可以让大家先睹为快。
如果大家对今年双十一主会场的展示效果和体验还有印象的话,那就是我们 Weex 技术方案的首秀――当然这个页面已经下线了。如果大家错过了或还想再回味一下,我们有另外一个线上的页面可供参观:今天大家用手机淘宝扫码打开下面这个地址:
不论是安卓还是 iOS 客户端,甚至是普通浏览器打开,都可以看到相同的“秋意渐浓填新装”的活动,这是目前大家可以看到的一个用 Weex 渲染出来的界面。
如何用 Weex 写出无线端动态界面
这样的界面是如何开发出来的呢?言归正传,给大家介绍一下 Weex 界面的制作过程――从最基础的写法开始
Hello World
Hello World
屏幕上就会展示一段“Hello World”的文本
我们把要展示的内容放在了 的标签里,这里的内容是一个文本标签
,标签里的内容正是要展示的文本。
加特技:CSS 样式
我们可以通过 style
特性给文本设置一些 CSS 样式:
Hello World
或通过在外层增加 样式表:
Hello World
现在我们加入一张图片,并且通过 flexbox 布局方式使其左右排布:
一个超赞的宝贝标题
加入交互:JavaScript 数据和方法
点击商品信息可以打开商品详情:
<script> module.exports = { methods: { gotoDetail: function () { this.$openURL('https://item.taobao.com/item.htm?id=520421163634') } } }</script> 一个超赞的宝贝标题
这里的 <script>
标签是我们引入的可以用 JavaScript
定义数据和方法的地方, 标签里的
onclick
事件会绑定 <script>
里的 gotoDefault()
方法,这个方法里的 $openURL(url)
是一个我们内置的 API 方法,可以打开一个网址。
数据绑定
如果同一个页面中会出现多个商品,还可以通过数据绑定的方式避免进行复用:
<script> module.exports = { data: { itemList: [ {itemId: '520421163634', title: '宝贝标题1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'}, {itemId: '522076777462', title: '宝贝标题2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'} ] }, methods: { gotoDetail: function () { this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId) } } }</script> {{title}}
这里我们通过双大括号的语法将 中要展示的特性或内容和
<script>
中的数据和方法绑定在了一起。
自定义组件 & 组件化开发
如果页面结构比较复杂之后,我们还可以通过一些自定义组件来将大的元素拆分成一个个小的元素,以此增强代码的可复用性、易读性、可扩展性:
<script> module.exports = { data: { itemId: '', title: '', pictureUrl: '' }, methods: { gotoDetail: function () { this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId) } } } </script> {{title}} <script> module.exports = { data: { itemList: [ {itemId: '520421163634', title: '宝贝标题1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'}, {itemId: '522076777462', title: '宝贝标题2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'} ] } }</script>
基本的 Weex 开发方式就是这些了,想主会场这样如此复杂的界面,其实也都是这样的代码组合而成的。
下面来介绍一下技术解决方案:Weex
一句话介绍
Weex 是一款轻量级的移动端跨平台动态性技术解决方案!
几个特点
轻量
体积小巧,语法简单,方便上手
可扩展
业务方可自行横向定制 native 组件和 API
高性能
快速加载,快速渲染,体验流畅
其它
拥抱标准:基于 Web 标准设计语法
响应式界面: 通过简单的模板和数据绑定轻松解决数据和视图的同步关联问题
多端统一:iOS、Android、HTML5 多端效果一致,撰写一次就可以轻松达到跨平台的一致性,无需针对多套平台单独开发,省时省力复杂逻辑描述:动态性不只体现在展示效果的动态性上,更体现在可以实时调整复杂的数据处理方式和逻辑控制方式
组件化:组件之间通过 webcomponents 的设计完美的隔离,并可以通过特定的方式进行数据和事件的传递
生态&链路:我们为 Weex 的开发者和使用者在不同维度上提供了各式各样的工具和平台,包括代码打包工具、开发者调试工具、部署平台、Playground、经典案例、入门指南和详尽的文档等。你不是从零开始,你也不是一个人在战斗!
如何工作
本地组件开发
首先,我们像开发 webcomponents 一样,把一个组件分成 、
显然这些代码是不会被 native app 识别的,我们要想办法让这些代码可运行。所以我们同时做了三件事:
在本地用一个叫做 transformer 的工具把这套代码转成纯 JavaScript 代码
在客户端运行一个 JavaScript 引擎,随时接收 JavaScript 代码
在客户端设计一套 JS Bridge,让 native 代码可以和 JavaScript 引擎相互通信
所以紧接着第二步,就是用 transformer 对代码进行转换,变成客户端可运行的 JavaScript 代码
图:本地开发时的 Weex Transformer 工作原理
其实本地开发还有一点很重要,就是把复杂的界面通过组件化的方式进行分解,并合理的建立组件之间的组合和调用关系。
最终,我们把简单组件组合成复杂的界面,并通过 transformer 打包成一个完整的程序包 (主体是一段 JavaScript 代码)
细节2:由于 Weex 组件的开发和 Web 组件的开发非常接近,但是对标准的支持范围和一些细节是有不同之处的,我们会贴心的在 transformer 里加入了一些友情提醒,帮助大家回避常犯的书写错误。
客户端渲染
上一节已经提到了,我们在客户端会运行一个 JavaScript 引擎并且有 JS Bridge 通信机制。这里再介绍具体一些:
native 渲染和 JavaScript 引擎之间,主要进行三类通信:
界面渲染,单向 (JS -> native):这毫无疑问,JavaScript 引擎需要把界面的结构和样式告诉 native 端,这样我们才能得到 native 级别的终极界面效果
事件绑定与触发,双向:在我们的客户端技术方案中,native 端只负责界面渲染和非常薄的事件触发层,事件的逻辑处理都会放在 JavaScript,这样我们就具备了复杂数据处理和逻辑控制的动态性可能。JS 告诉 native 需要监听的交互行为,而当用户产生对应的交互行为时,native 端会把交互信息回传给 JS
对外的数据/信息请求与响应,双向:JS 引擎在处理特殊逻辑时,难免需要向服务器请求数据、或请求本地的系统信息和用户信息、或调用 native 的某个功能,这个时候也会通过 JS Bridge 进行请求,native 收到这些请求之后,也会在必要的情况下通过 JS Bridge 把信息回传给 JS 引擎
图:客户端运行时的 JS 引擎和 native 之间的通信
再加上外层对 Weex 实例的管理,整套机制就可以顺畅的工作起来了
细节3:native 端渲染的时候,我们以图片和文字的形态为主,并大量依赖了标准的 CSS 样式进行细节的渲染
细节4:我们把框架层面的 JS 代码全部提前放在了客户端本地,并提前运行做好准备。这样本地生产的 JavaScript 是非常小的,网络传输的代价也非常低,而在客户端运行的初始化成本也非常低。整条链路都和界面打开速度息息相关
细节5:我们在 JS 处理界面逻辑的过程中采取了数据监听+依赖收集的策略,既没有通过脏检查,也没有通过全量 diff Virtual DOM 树的方式,因为通常在移动端,数据变更都是非常小量的,经过我们的实践,这套方案完全可以应付移动端日常的动态性界面需求
图:Virtual DOM 的数据绑定和更新机制
细节6:我们对业务上通用常用的组件进行了封装,并且暴露规范化的类型 (标签名)、特性、样式、事件、上下级约束等维度的定义。这样所有的业务界面都可以用这些基础的组件搭建而成
细节7:我们对业务上通用常用的 API 进行了封装,并且暴露规范化的 JS API
服务端部署
我们在服务端提供了基础的程序包发布,给每个程序包一个特定的 page id,然后为客户端提供通用的服务,通过 page id 获取程序包,这样本地开发、动态实时部署、客户端动态化渲染和逻辑处理就完美的串联在一起了
细节8:实际上,除了界面本身可以动态化之外,客户端的 JS 引擎的代码、还有部分 native 的实现,我们也准备了相应的动态化机制,也就是说客户端的动态能力本身也是具有动态性的
浏览器端渲染
我们还会面对这样的场景,就是一个客户端的业务,会通过微博之类的渠道进行转播和推广,当用户手机里刚好安装了手机淘宝客户端,那么会直接“拉起” 客户端进行相应的界面展示,如果没有装手机淘宝,则需要在浏览器里展示一个相同或接近的界面。自然 Weex 技术方案支持的业务也有这样的需求。所以我们同时提供了 HTML5 版本的技术方案,同一份 JavaScript 程序包,可以同时通过客户端的 JS Bridge 渲染成为 native 界面,也可以通过浏览器渲染成为 web 界面。我们的做法也非常简单,就是把 JS Bridge 背后的 native 处理逻辑同构成了 HTML5 版本。然后发布这样的一个页面。
图:HTML5 浏览器端的架构实现
细节9:我们能够同构 HTML5 版本和 native 版本,主要归功于我们在 JS Bridge、组件定义、API 定义方面的高度抽象――当然 HTML5 的版本在性能和体验上确实有一定的劣势,并不是最理想化的效果,所以核心的主战场还是客户端,这也和目前的移动互联网的形态相吻合
周边
为了方便 native 界面的调试,我们还提供了配套的开发者工具,稍后会有更多介绍,同时我们的上层配套可视化编辑器和装修工具,也在紧张的研发当中。
综上所述,整个 Weex 的工作原理大致可以用一张图来表述:
回看 Weex 的几个特点
轻量
我们致力于把开发体验、网络传输的大小、运行时的开销控制做到极致,并且尽可能的降低多端适配和优雅降级的成本
横向可扩展
我们对组件的定义和业务功能预留了很好的横向可扩展能力,这也业务方可以自由定制属于自己的 native 组件和 API,从而在后期可以通过实时发布不同的程序包来进行动态化控制。同时也因为它的横向可扩展性,Weex 的核心可以非常小,非常易于融入现有的无线技术体系
高性能
我们在网络传输、实例初始化、JS 运算、native 渲染能力等方面做了非常针对性且深入的优化,尤其是针对中低端安卓机,不论是加载时间还是运行时的流畅度,都比之前的方案有质的飞跃。我们对 CPU、内存、帧率、首屏渲染时间等核心性能指标也一直保持高度的关注,也建立了相应的线上监控和数据统计机制。而更多可优化的空间和方案我们还在不断的进行优化尝试。
下图是今天凌晨旧金山 QCon 上 Weex 技术方案的首次公开分享中的一页性能表现对比图,大家可以感受一下:
总结
目前 Weex 还在努力达到更高的性能、更高的扩展性、更低的开发成本、更完整的生态和工具链,也同时尝试接入更多的业务,体现出它的更大的价值。已经有很多业务方迫不及待的在和我们主动取得联系了,未来我们希望 Weex 能够逐步在集团内开放试用,并最终走向开源。