千牛工作台是做什么的(Weex在千牛开放中的应用实践)

更多深度文章,请访问云计算频道:https://yq.aliyun.com/cloud

2017年1月12日,在WEEKONF 2017上,阿里巴巴事业部无线牛倩团队以阿里巴巴无限业务的实际业务,分享了牛倩开业一周的应用实践。本文分享了业务面临的挑战,无线牛倩团队如何一步步转向WEEK,以及在实际过程中遇到的挑战和做出的努力。本文是Weex在牛倩公开赛应用实践的分享和整理。

本文整理自演讲嘉宾的分享视频和PPT。

本次分享将主要介绍Weex在牛倩开放平台的一些应用实践。今天分享的内容主要分为以下四个部分:

  • 牛倩当前的商业场景和挑战

  • 为什么选择Weex作为解决方案?

    基于Weex解决方案我们还做了什么?

    目前,改造后isv使用效果与目前进度的对比。

    让我们先介绍一下牛倩目前的业务情况。你可能不熟悉牛倩的客户。实际上,牛倩是企业的移动客户端。牛倩主要是淘宝商家的一站式管理店铺和办公平台。下图中的右图是牛倩客户端的截图。



    淘宝商家可以在牛倩的平台上接受客户服务,管理自己的店铺,阅读数据报告。最重要的一点是,商家还可以支持所有与自己店铺相关的工具链接,比如交易管理的工具和服务,商品管理的交易和服务等。这些服务和管理都是由牛倩平台的isv提供的,因为isv可以根据用户的行为为用户提供各种定制服务,所以我们把这部分交给isv来实现。这样,牛倩的整体工作平台就基本建成了。为了实现这样的一站式工作平台,牛倩提供了开放的插件系统,帮助ISV开发更丰富的工具。此外,牛倩平台还做了用户画像,会根据画像将用户划分到不同的用户域和不同的角色,让不同的用户看到不同的、动态变化的工作站,并且会为不同的用户提供不同的功能。

    接下来解释插件的应用场景。牛倩是一个数据驱动的平台。当淘宝里一个交易订单的数据发生变化时,系统会收到一条消息,这条消息会推送到牛倩的客户端,然后客户端的数字区域就会发生变化。当用户发现客户端上的号码有变化或者收到新消息时,可以通过点击号码或者消息跳转到isv的插件,然后插件完成isv提供给用户的一些具体任务,比如查看订单的具体细节,复制订单的具体发货地址,修改商品价格等。这些功能都是在isv提供的插件内部完成的。插件完成业务处理后,会跳回牛倩的模块,比如像旺旺这样的接收或通信,接收后再跳转到isv的插件继续处理。对于用户来说,看到的是牛倩客户端的整体操作流程,但在牛倩内部,其实是从牛倩跳到isv的插件应用,再跳回牛倩的三步动作。所以isv提供的插件的稳定性和用户体验的一致性将是一个需要我们关注的问题。



    接下来是多角色和多用户域的演示。从下面两张图可以看出,牛倩的工作平台为不同用户提供的服务和显示的界面是不同的。在所有的工作站中,在所有的O2O场景中,用户看到的界面都可能是左图这样的。这个界面不会有太多的数据,它更重要的工具会被突出显示。对于企业的老板来说,他可能更关心的是与企业业务相关的数据,比如企业的订单数量。因此,对于不同的用户域和不同的用户角色,需要动态构建牛倩的客户端。



    那么牛倩在使用Weex之前提供了什么呢?其实之前,我们把平台分为四层。第一层是设备能力层,即通过容器层开放牛倩的拍照、网络、存储、定位等基础能力。将这些能力开放给ISV,然后通过容器层将ISV的业务代码与牛倩的代码进行沟通和呈现。上层提供了MSUI组件库和离线包系统。离线包类似于前面提到的资源预加载。顶层是牛倩的业务能力层。在这一层,牛倩能够提供的所有业务能力都被API抽象和输出。这样,isv就可以通过API调用使用牛倩官方提供的能力,比如改价、退款、支付、旺旺接收等功能。



    牛倩面临的挑战是什么?事实上,众所周知,在H5发展模式下,我们需要面对几大挑战。一个挑战是网络,对isv影响很大。另外,就是动画。事实上,ISV很难实现一些类似于native的交互动画。第三个挑战是安全性,因为一些运营商的劫持会极大的扰乱ISV。另外,ISV本身的问题也是要面对的挑战,因为ISV本身的水平有高有低,参差不齐。比如有些ISV可以较好的解决网络和动画问题,可以实现接近原生的交互用户体验,而技术差的ISV往往做不到。



    另一个挑战是isv发布的问题。因为ISV可以直接在服务器端发布自己的插件。也就是说,一旦isv入驻牛倩平台,它的插件可以随心所欲地更新。如果isv在其插件中挂出广告或活动页面,即使极大地损害了用户体验,平台对其也没有太多的控制和约束。因此,对于牛倩平台来说,如何提高isv控制能力也是一个很大的挑战。另外,如前所示,牛倩平台的动态性也很强,需要针对不同的用户域展现不同的用户形态。这些都是成千上万的奶牛需要面对的挑战。

    那么我们为什么选择Weex呢?其实在接入Weex之前,我们也在React Native上进行了一些尝试。React原生方案提出后,我们觉得很好,可以很大程度上解决我们当时开发方式带来的问题。因此,2015年11月,我们开始试用自己的业务代码,经过验证,发现在长列表加载速度等问题上可以满足要求,于是2016年3月,我们开始尝试将其打包,提供给isv。但是在开发和接入的过程中不断暴露出问题,最后不得不切换跑道,投入Weex的怀抱。

    接下来说一下我们在React Native遇到的问题。首先是性能问题。随着业务的日益复杂,捆绑包的大小会成为更大的问题,捆绑包会不断膨胀,需要下大力气优化。更可怕的是,捆绑包的大小会直接影响加载速度。我们做过一个对比实验:当业务足够复杂,捆绑包足够大时,首次加载时间甚至比一个Webview还要长。另外就是React Native自身的问题,比如核心组件Listview中的单元格重用,会影响内存开销和性能。当然,这些问题在今天看来可能有一些解决的办法,但对当时的我们来说确实是一个巨大的挑战。



    React Native的另一个问题是内存消耗。因为每个插件都是一个独立的环境,需要频繁的创建和销毁实例,所以内存消耗会很巨大。

    第三个问题是结尾的不同。React Native强调的是一次学习而不是一次编写,所以意味着isv无法关心平台上的差异,消除平台上差异的工作只能由平台自己来处理,但这些问题并不是不可克服的。最后让我们放弃React Native的是它的破改问题,因为React Native是一个开发速度很快,更新频率很高的框架。在我们接入的阶段,React Native基本上一到两个星期就会更新一次,两次更新之后就会出现一个破变问题,这给我们带来了很大的挑战。因为如果你只使用React Native开发自己的业务,可能不会有太大的问题,但是如果你使用React Native为其他ISV搭建平台,那么破改的问题就会让开发者非常头疼。如果每一次改动都需要通知平台所有ISV进行版本更新或升级,成本将难以承受。

    基于以上原因,Weex走进了我们的视野。Weex吸引我们的有以下几点:第一,版本升级的前向兼容性,Weex团队承诺框架的前向兼容性。第二点是Weex具有多终端一致性,我们不需要自己处理多终端一致性问题。Weex的第三个优点是轻量级,支持可插拔能力,扩展方便。Weex提供了基本的功能,但是这些功能可能无法满足每个业务场景。而且像牛倩这种面向业务的业务场景,我们需要对自己有更多的业务考虑,所以我们非常重视这个框架是否可以轻松替换,是否可以提供基础能力和扩展能力,Weex在这方面做得非常好。另一方面,Weex解决了React Native本身的一些性能问题,比如Bundle运行环境的共享,Listview的复用也得到很好的解决。最后,Weex可以灵活支持多种前端框架,无需切换前端框架,因为我们之前在React Native上做了很多工作,如果不能实现平滑过渡,会给我们带来很多麻烦,而Weex可以帮助我们平滑迁移。



    至于我们用Weex做了什么,其实在QAP2.0时代,我们主要做了三个方向的工作。一是插件APP优化打开主链接;二是为新的开发方式提供更丰富、更适合的能力;再次,新框架出台后,希望帮助牛倩平台上的ISV以更友好的方式迁移,尽快熟悉新框架。

    这样就产生了下图所示的框架。客户端的基本分层并没有改变。此外,容器层还加入了web容器,做了一些相关的工作打通容器,比如使Weex容器中原本为ISV开放的Web容器无缝使用成为可能。这种Web容器可以被视为导航页面或组件。此外,它为新的开发方法提供了一些新的框架,如包管理的相关机制,并提供了更原生的存储机制、数据收集、导航管理和插件生命周期管理等。此外,我们还提供一整套与开发相关的工程管理工具。工程管理工具的目的主要是在安装、打包、发布的一系列过程中遵守包的规范和格式,同时也为isv提供一些开发和调试工具。



    接下来我们回到外挂APP开启主链接优化的话题。我们把主要的链接优化分为三个环节:资源加载、数据加载和页面渲染。

    对于资源加载,在QAP1.0中,我们为ISV提供了将网络请求与本地资源相链接的能力。插件容器发起请求时,传统的方式是直接向isv的云端请求资源,而QAP1.0中提供了离线包的机制,插件容器可以先请求离线资源包,看看资源包中是否有需要的资源,然后通过牛倩的云端将相关资源分发给客户端 也就是QAP1.0的资源加载机制,QAP2.0版本彻底废除了插件容器直接向isv请求资源的链接,只有在服务降级的情况下才有可能使用这个链接。 因此,新的开发方法对包管理的要求非常严格。以前资源包可能是可选的,因为确实无法通过网络请求资源。但QAP2.0将资源包升级为应用程序,成为插件容器请求资源的主战场。相比之前资源包中JS或CSS之类的静态资源,QAP2.0版本的资源包中有很多配置文件,可以对资源配置安装包的特定能力或动作。



    可以看看QAP2.0中的包结构是什么样子的。这个包主要由两部分组成,第一部分是资源目录,另一部分是配置文件。



    资源目录中存储的其实是各个配置的捆绑包,还有静态的资源图片和icon之类的资源。至于配置文件,也分为三个部分。第一部分是与APP相关的配置,存储了APP配置的一些基本信息,比如插件的AppKey,容器的相关信息,引入的NUKEUI,QAPSDK的版本和默认的应用启动页面等。另一部分是APP的能力声明配置。其实在数字区后面,有一个插件。我们抽象出数字在业务中的对应行为,提取不同的能力供isv实现。如果isv要实现某项业务,需要告诉客户端,当一个插件的能力被触发时,它会唤起这个插件来帮助客户端执行这项工作,即声明APP的能力,并与外部的能力路由连接。第三是资源配置。当软件包在本地安装和分发时,它将读取资源配置中的配置表,并将资源分发到正确和合适的位置。这个位置对开发者来说是透明的,但是会大大方便平台的管理。

    千牛工作台

    说完了资源加载,我们来说说数据请求。说到数据请求,先说说isv在之前开发过程中遇到的问题。为了完成一项业务,一个isv实际上需要调用多个API来获得想要的结果。另外,为了保证数据的时效性,需要经常拉取。因为不知道数据什么时候更新,每次都需要拉取,导致缓存利用率低。另外,在之前的架构中,缓存需要考虑用户隔离、大小限制等问题,使用起来会变得非常麻烦。



    接下来分享一下QAP上提供了什么样的数据请求等待优化方案。QAP获取数据不是靠自己,而是靠淘宝的top平台,所以我们对top请求做了一个长连接代理,用TCP长连接解决了HTTP的复用等耗时过多的问题,此外还利用了web浏览器本身提供的存储能力。

    在QAP2.0中,我们引入了批量顶请求,即批量发送和接收多个请求。另外,对于数据推送,TCP长连接中有一个数据推送通道,在QAP2.0中开放给isv,isv可以实时将其重要数据推送到客户端,不用每次都去拉想要的数据。而且实现了多用户数据的自然隔离,提供了批量存储接口。

    在页面渲染部分,我们做的工作相对较少,主要是给予其他团队支持。QAP1.0用的是MSUI+Webview,QAP2.0用的是NukeUI+Weex,这里稍微说一下NukeUI的优势。NukeUI有更规范的UI交互,提供了千牛组件开发的规范,以及主题换肤能力和更好的性能。

    在做了这些事情之后,在推出组件和包装QAP时仍然存在许多问题。其中之一是包装的大小。目前牛倩平台上的插件功能很多,非常复杂。isv开发的页面不像运营页面,可以看作是由多个页面组成的APP的一个程序,所以包大小很难控制。另外,每个千牛用户平均有20多个插件,插件安装包也很多。如果套餐大小控制不好,会对用户的流量和空造成巨大的消耗。捆绑包的大小也会在一定程度上影响插件的性能。捆绑包主要包括像rax、components、SDK这样的几个部分,所以我们可以看到对于每个包,都有很多公共部分。虽然rax是按需打包的,但是随着业务越来越复杂,一个APP几乎可以覆盖组件库中的所有组件,所以可以把这部分提取出来作为通用部分。



    所以我们的解决方案是把常用的部分提取出来放到Main.js中,main . js是Weex的运行环境,我们把它们内置到Weex的运行环境中。在提取了通用部分后,捆绑包中只剩下isv业务代码,使得包的大小得到了很好的控制。当然,这种方式带来的更大挑战是内置包的版本管理和更新。因为Weex容器的初始化时间是加载程序APP的时候,所以在程序运行过程中没有办法做任何事情。



    目前我们正在努力的方向和想要解决的问题是内置包的管理和动态升级,这需要Weex框架的支持。Weex最近提出了Server的概念,它允许内置的包在插件打开之前运行在如图所示的环境中。如果配置环境中的配置信息与客户端的版本号一致,则默认构建该环境。如果不一致,会动态更新有问题的包,这样就可以解决内置包的管理和更新问题。



    千牛也提供了更丰富的能力。在通信机制方面,这里的通信机制主要是针对页面层面和整个插件的生命周期。第一个页面级别,即单个包中的事件通信;其次,插件APP级事件通知,即插件APP下所有Bundle之间的通信;第三种是牛倩应用层面的事件通知,会通知开发者牛倩主应用发来的全局事件,比如前后端的切换、网络的变化等。此外,我们还提出了粘性事件的概念,即在事件没有被消费的时候,事件不会丢失,以避免在某些场景下事件发出去了,却因为还没有注册而收不到的情况。



    AppIndex主要解决能力声明和能力路由的问题。整个插件能提供什么能力,如何路由,都由AppIndex管理。首先,对于一个插件来说,它可以将其对应的能力输入到能力路由表中,并在AppIndex客户端系统中注册。在进行能力路由时,它会通过权限管理和能力路由找到能够实现这个能力的插件,根据路由表找到具体的页面。其实在QAP1.0中就有这样的机制,只是当时只能返回程序主入口的地址,而在QAP2.0中,每一页都可以精确定位。



    为了地址准确,牛倩的客户端提供了QAP的通用URI。所有QAP页面都由下图中的这套URI机制寻址,每个页面都可以由这个URI定位。



    最后,我们做了大量工作来降低isv的迁移成本。我们实现了Weex+Web的混搭,即在前期推广时,允许ISV对自己的部分页面进行改造,即对其插件的入口页面和性能要求高的页面进行Weex改动,其他要求低的页面也可以使用Web,这样可以降低ISV的开发成本;此外,它还为isv提供业务开发实例、开发文档和一站式开发工具。

    在开发工具方面,我们提供了QAP的CLI,可以实现创建项目、导入样例项目、调试、打包上传、安装真机测试、获取最新测试包等功能。但是在给isv提供CLI的时候发现了一些问题,比如平台环境的问题,安装慢的问题。

    针对以上问题,我们现在做的就是把整套相关依赖做成IDE,让开发者在IDE中一站式完成,找到入口,创建项目,调试项目,一键安装上传。



    您可以还会对下面的文章感兴趣

    使用微信扫描二维码后

    点击右上角发送给好友