【转载】对无线电商动态化方案的思考(一)

发布于 大漠

如题,接下里的一段时间里,我会从无线电商动态化的角度谈一谈自己的理解和思考

无线电商动态化

无线

首先要谈的是无线,或者说移动。手机和传统桌面端计算机有着非常大的不同,首先是随时随地,这样它就需要同时也可以拥有更多的传感器,比如镜头、体感、定位、蓝牙、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 的方案设计和开发方式,当然在此之前,Weex 能够做出什么样效果的界面,可以让大家先睹为快。

Weex

如果大家对今年双十一主会场的展示效果和体验还有印象的话,那就是我们 Weex 技术方案的首秀——当然这个页面已经下线了。如果大家错过了或还想再回味一下,我们有另外一个线上的页面可供参观:今天大家用手机淘宝扫码打开下面这个地址:

DEMO

Weex

不论是安卓还是 iOS 客户端,甚至是普通浏览器打开,都可以看到相同的“秋意渐浓填新装”的活动,这是目前大家可以看到的一个用 Weex 渲染出来的界面。

Weex

如何用 Weex 写出无线端动态界面

这样的界面是如何开发出来的呢?言归正传,给大家介绍一下 Weex 界面的制作过程——从最基础的写法开始

Hello World

<template>
  <text>Hello World</text>
</template>

Weex

屏幕上就会展示一段“Hello World”的文本

我们把要展示的内容放在了 <template> 的标签里,这里的内容是一个文本标签 <text>,标签里的内容正是要展示的文本。

加特技:CSS 样式

我们可以通过 style 特性给文本设置一些 CSS 样式:

<template>
  <text style="color: #ff0000;">Hello World</text>
</template>

Weex

或通过在外层增加 <style> 样式表:

<template>
  <text class="title">Hello World</text>
</template>

<style>
  .title {color: #ff0000; font-size: 48; font-weight: bold;}
</style>

Weex

现在我们加入一张图片,并且通过 flexbox 布局方式使其左右排布:

<template>
  <container>
    <image class="thumb" src="http://gw.alicdn.com/tfscom/TB1OMfAJFXXXXbfXXXXqVMCNVXX-400-400.jpg"></image>
    <text class="title">一个超赞的宝贝标题</text>
  </container>
</template>

<style>
  .thumb {width: 200; height: 200;}
  .title {flex: 1; color: #ff0000; font-size: 48; font-weight: bold; background-color: #eeeeee;}
</style>

Weex

加入交互:JavaScript 数据和方法

点击商品信息可以打开商品详情:

<template>
  <container onclick="gotoDetail">
    <image class="thumb" src="https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg"></image>
    <text class="title">一个超赞的宝贝标题</text>
  </container>
</template>

<style>
  .thumb {width: 200; height: 200;}
  .title {flex: 1; color: #ff0000; font-size: 48; font-weight: bold; background-color: #eeeeee;}
</style>

<script>
  module.exports = {
    methods: {
      gotoDetail: function () {
        this.$openURL('https://item.taobao.com/item.htm?id=520421163634')
      }
    }
  }
</script>

Weex

这里的 <script> 标签是我们引入的可以用 JavaScript 定义数据和方法的地方,<template> 标签里的 onclick 事件会绑定 <script> 里的 gotoDefault() 方法,这个方法里的 $openURL(url) 是一个我们内置的 API 方法,可以打开一个网址。

数据绑定

如果同一个页面中会出现多个商品,还可以通过数据绑定的方式避免进行复用:

<template>
  <container style="flex-direction: column;">
    <container repeat="{{itemList}}" onclick="gotoDetail">
      <image class="thumb" src="{{pictureUrl}}"></image>
      <text class="title">{{title}}</text>
    </container>
  </container>
</template>

<style>
  .thumb {width: 200; height: 200;}
  .title {flex: 1; color: #ff0000; font-size: 48; font-weight: bold; background-color: #eeeeee;}
</style>

<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>

Weex

这里我们通过双大括号的语法将 <template> 中要展示的特性或内容和 <script> 中的数据和方法绑定在了一起。

自定义组件 & 组件化开发

如果页面结构比较复杂之后,我们还可以通过一些自定义组件来将大的元素拆分成一个个小的元素,以此增强代码的可复用性、易读性、可扩展性:

<wx-element name="taobao-item">
  <template>
    <container onclick="gotoDetail">
      <image class="thumb" src="{{pictureUrl}}"></image>
      <text class="title">{{title}}</text>
    </container>
  </template>
  <style>
    .thumb {width: 200; height: 200;}
    .title {flex: 1; color: #ff0000; font-size: 48; font-weight: bold; background-color: #eeeeee;}
  </style>
  <script>
    module.exports = {
      data: {
        itemId: '',
        title: '',
        pictureUrl: ''
      },
      methods: {
        gotoDetail: function () {
          this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId)
        }
      }
    }
  </script>
</wx-element>
<template>
  <container style="flex-direction: column;">
    <taobao-item repeat="{{itemList}}"></taobao-item>
  </container>
</template>
<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 开发方式就是这些了,想主会场这样如此复杂的界面,其实也都是这样的代码组合而成的。

整个开发方式应该还是蛮简单清晰的吧,总体上是一个“傻瓜版”的 HTML / webcomponents 式开发,但可以做出 native 级别的体验。

今天再补充这么多咯,大家真的太热情了,我觉得有点愧对大家的期待,所以今天再多介绍一些,但 Weex 背后的实现原理,特点和优势,以及值得探讨的技术细节还有很多:)

如需转载,烦请注明出处:https://github.com/amfe/article/issues/13Nike kyrie 2 Rainbow