WEEX依旧采取传统的web开发技术栈进行开发,同时app在终端的运行体验不输native app。其同时解决了开发效率、发版速度以及用户体验三个核心问题。那么WEEX是如何实现的?目前WEEX已经完全开源,并捐给Apache基金会,我们可以通过分析其源码来一探究竟。
传统的移动端开发,一个完整的业务需要维护三份终端代码:Android、iOS、H5,这带来了极大的开发成本以及维护成本。尤其是对处于业务初创期需要快速试错的业务以及需要支持定期运营活动的业务。所以业界也一直在探索跨平台方案,旨在通过一套代码完成各个终端的业务逻辑。相关方案经过不断演化,从早期的H5、Hybrid到如今的Cloud Native(云原生),在开发效率和用户体验上都在一点点逼近最初的设想。
早期H5和Hybrid方案的核心是利用终端的内置浏览器(webview)功能,通过开发web应用满足跨平台需求。该方案可以解决跨平台问题,同时可以提升发版效率。但其最大的弊端在于用户体验相较于native开发的app存在较大差距,经常出现页面卡顿,加载慢等问题。
于是后来业界开始探索依旧利用web技术栈开发出媲美原生体验app的方案,于是以WEEX为代表云原生开发框架开始出现。所谓云原生(Cloud Native)指可以通过云端快速发布(与远程web应用发布流程类似),同时还可以达到媲美原生App体验的方案。WEEX依旧采取传统的web开发技术栈进行开发,同时app在终端的运行体验不输native app。其同时解决了开发效率、发版速度以及用户体验三个核心问题。那么WEEX是如何实现的?目前WEEX已经完全开源,并捐给Apache基金会,我们可以通过分析其源码来一探究竟。
WEEX框架主要分为两部分:
前端JavaScript框架
Native SDK
本文主要探讨Native SDK的核心原理,其前端JavaScript框架会在后续的文章中进行介绍。
1 整体架构
首先来看下WEEX开发的整体架构:
从上图中可以看到weex的大致工作流程:
研发人员利用web技术栈开发weex file,打包成JS Bundle,然后部署到服务器上
终端通过网络获取JS Bundle,然后在本地执行该JS Bundle
终端上提供了JS的执行引擎(JSCore)用于执行远程加载到JS Bundle
JS执行引擎执行JS Bundle,并将相关渲染指令以及其他需要利用native能力的指令通过JS-Native Bridge透出
JS-Native Bridge将渲染指令分发到native(Andorid、iOS)渲染引擎,由native渲染引擎完成最终的页面渲染
看完上述整体架构后,可以大致理解为何WEEX可以达到媲美原生的体验,因为其页面渲染并不是像H5方案一样接入浏览器的渲染能力,而是原生渲染,所以本质上渲染出来的页面就是一个native页面。
接下来我们再来将端上的模块进行详细的拆分:
如上图所示,WEEX NATIVE SDK大致可以分为如下几个层级:
JS执行层:
JS执行引擎:JSCore,解释并执行JS Bundle
main.js:提供WEEX runtime,SDK初始化,JS Core会首先加载main.js,为js bundle提供weex runtime
Bridge层:提供JS和Native的双向通信能力
Dom层:维护页面Dom结构
Render层:完成页面渲染
native组件库:本地UI组件库,每一个组件对应一个html标签,所以当我们在weex开发过程中使用到的各种标签:div、text、image等等,最终都被转化成为了一个native的控件
module manager、module库:功能模块管理层
WXSDKManger、WXSDKEngine:SDK全局环境维护
WXSDKInstance:weex 实例,一个js bundle对应一个weex实例
2 WEEX SDK初始化
有了上述大致架构和功能划分后,我们以一个实际的例子来分析WEEX NATIVE SDK的运行逻辑。首先来看下WEEX SDK在初始化阶段都做了哪些准备工作。
这里以Andorid代码为例进行分析:WEEX的初始化通常放在Application中,其初简化的初始化逻辑入如下:
public class WXApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
initWeex();
......
}
private void initWeex() {
// 自定义相关配置
InitConfig config=new InitConfig.Builder()
.setImgAdapter(new ImageAdapter()) // 自定义图片适配器
.build();
WXSDKEngine.initialize(this,config);
// register module
try {
WXSDKEngine.registerModule("testmodule