浅谈混合应用的演进

前端想要写 APP

开篇想以这样的方式开头,从 APP 开始火到如今,前端同窗就一直想要写 APP,各方技术也是为了让前端同窗写上 APP 操碎了心。javascript

为何要前端同窗来写 APP,站在整个技术链上来看,都是在作页面呈现,页面交互,对于技术而言只是将产品在不一样的端上进行呈现,因此很早以前就有提倡说大前端的概念。既然功能都差很少,就没有分家的理由。前端

再者,开发客户端如今的常规平台有 iOS 和 android 两个大的平台,在前端领域愈来愈细分的大环境下,传统意义上的客户端开发至少须要两拨开发者来作,成本就会有增长。而且伴随着成本增长还带来了须要两套体系化的语言开发,开发效率也会大打折扣。相较于两大客户端平台的开发人员,前端的开发人员是要更多的,而且随着移动端的兴起前端开发者会有资源剩余的状况,前端来写 APP 也能够平衡资源。java

因此不只仅是前端同窗想要来写 APP,更多的是将来发展须要这样的技术来提升生产力,因此才会有各类各样的方案出现,各类方案的诞生也发现了前端写 APP 的不足,因此又有了不断的演进来适应将来发展。送给每个前端人,咱们的征途是星辰大海android

web APP

就是咱们常说的 h5 应用,这个阶段属于知足展现功能阶段,首先是解决手机端能展现页面的问题。git

实现方式就是客户端提供 webView 组件,就是一个基于 webkit 引擎、展示 web 页面的控件。web APP 实际上就是客户端提供了一个浏览器的宿主环境,能在手机上显示对应的网页内容,可是各大手机厂商对于内核都会有所谓的“优化”,这些所谓的优化或者内核版本升级所带来的兼容性问题,也是让前端同窗咬牙切齿。github

客户端提供了相似浏览器的宿主环境,前端将本身的 HTML/CSS/JS 运行起来,就能够看到页面了。可是在 PC 端原来咱们看到的页面放在手机上,展现和交互都不太合适。之因此咱们要叫 h5 应用,是说咱们在移动端上须要用到 HTML5/CSS3 的新功能,经过这些功能能在客户端的浏览器上作适配展现页面。web

响应式的布局方式,新式的 meta 标签,媒体查询等功能让前端之前显示在 PC 端上的页面能展现在手机端。随着 PWA 的出现,如今手机端逐渐对 Service Worker 的支持(Safari 还在踌躇犹豫),web APP 又从新焕发了青春。ajax

优势:chrome

  • 基于 webView 的跨平台,调试方便(控制台调试)
  • 开发速度快,只要有浏览器就能打开,适合小版本的试错
  • 无需安装,不占内存,随时更新,维护成本低

缺点:json

  • 打开白屏时间长,用户体验较差,交互功能受限。
  • 产品也受限于浏览器,而且留存低,适合拉新
  • 不采用 PWA 的方案就没法离线,没有网络就失去了活力,如今不用网络的 APP 状况已经不多了。重复打开重复加载

表明产品:

  • 全部可使用手机浏览器打开的网页(不作兼容展现会有问题)
  • 微信公众号里纯文章

hybrid 应用

跨过了 web APP 的知足展现功能阶段,这个时候开始想有更多的功能知足交互需求,好比调起原生的摄像头(虽然 input 也能够实现),这个时候催生了 hybrid APP,也进入了丰富功能阶段。

前端 webView 的交互实现没法直接与摄像头的 API 进行交互,相似这样的功能是客户端的能力,webView 想要完成这样的功能,若是不能直接完成是否是能够经过调用客户端来完成这样的功能,可是前端和客户端是两种不一样的语言及实现方式,如何进行通讯?

想要通讯完成通讯,其实就是须要再前端与和客户端搭建一个沟通桥梁,就是如今常说的 js bridge。介绍击中常见的 js bridge 通讯方式。

js bridge

js 调用 native:

  • 请求拦截
  • 弹窗拦截
  • 注入 js 方法

native 调用 js:

  • 直接执行 js 代码

请求拦截

webView 发送请求都会通过客户端的请求发送模块,客户端能够在请求发送出去以前作拦截,你们约定好一种 URI 的实现协议,若是符合实现协议的请求,就拦截下来进行约定协议解析,其余的就当作真正的请求发送出去。

首先约定一个简单的协议

eros://bmImage/camera?params={"imageNum":2,"allowCrop":true,"callbackId":"bmImage.camera1527235458519"}

eros            // 做为协议存在,用于作拦截的方式
bmImage/camera  // 用于约定是调用客户端的方法名,能够是想要调用相机上传两种图片的接口
params          // query 表示参数,其中有一个参数名叫 params 的参数是一个 json,是调用客户端的方法所须要的三个参数
复制代码

无论 js 如何封装,就是须要发送一个请求出去,最好前端能作统一封装经过 iframe 或者直接经过 ajax 发送请求。

客户端如何拦截

android 的拦截方式 shouldOverrideUrlLoading

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    // 根据协议判断是否须要拦截
    if (true){
      // 解析协议路径得知调用方法
      // 解析 query 获得参数
      // 经过反射的方式去调用对应的原生方法
      return true;
    }
    return super.shouldOverrideUrlLoading(view, url);
}
复制代码

iOS 的拦截方式(UIWebView) webView:shouldStartLoadWithRequest:navigationType:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    // 根据协议判断是否须要拦截
    if (true){
        // 解析协议路径得知调用方法
        // 解析 query 获得参数
        // 经过 AOP 的方式去调用去调用对应的原生方法
        return NO;
    }
    return YES;
}
复制代码

上述过程就完成了 js 向 native 的通讯的一种方式,客户端完成了操做以后如何告知 js 后面会讲到。这种方式很差的地方在于只能支持异步的调用方式,网络 I/O 就决定了这种方式不支持同步。既然是 URI 的协议就会有长度限制,超长了请求就会被截断

更为严重的是无序性和有丢消息的可能,这也致使了这个方式很是不稳定,也是早期没有其余选择使用的方式。

弹窗拦截

js 调用弹窗时通常有 alert/confirm/prompt 三种弹窗,这三种弹窗对应客户端都会有方法实现,能够直接作拦截。

var actionInfo = {
    type: 'eros',
    action: 'bmImage/camera',
    params:{
        "imageNum":2,
        "allowCrop":true,
        "callbackId":"bmImage.camera1527235458519"
    }
}

prompt(JSON.stringify([actionInfo]))
复制代码

android 拦截 prompt

@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
    // 解析传过来的字符串判断 type 类型是否是解析的方式
    if (true){
        // 解析协议路径得知调用方法
        // 解析 query 获得参数
        // 经过 AOP 的方式去调用去调用对应的原生方法
        // 若是是异步返回
        return true;
        // 若是是同步,待上面的程序执行完毕以后返回结果
        // return res
    }
    return super.onJsPrompt(view, url, message, defaultValue, result);
}
复制代码

iOS 拦截 prompt(使用 WKWebView)

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler{
    // 解析传过来的字符串判断 type 类型是否是解析的方式
    if (true){
        // 解析协议路径得知调用方法
        // 解析 query 获得参数
        // 经过 AOP 的方式去调用去调用对应的原生方法
        // 若是是异步返回
        NSInvocation *invocation = [执行方法]
        [invocation invoke];
        return invocation;
        // 若是是同步,待上面的程序执行完毕以后返回结果
        return nil
    }else{
        执行对应的弹窗
    }
}
复制代码

这种方式比请求拦截好的地方是能够支持同步调用了。可是 iOS 中有两种 UIWebView 和 WKWebView,前者在 webView 中屏蔽的弹窗的功能。后者虽然内存要比前者控制的好可是有不少兼容性的问题。

js 注入上下文

上面的使用方式仍是比较曲折,发展到如今有了 JavaScriptCore,也是如今大部分混合应用解决方案核心,JavaScriptCore 没有 BOM 对象也没有 DOM 对象,甚至还缺失一些浏览器的方法,可是这块是 js 和 native 都能访问到的公共区域。在打开 webView 以后,往 js 的上下文中注入方法,供 js 进行调用。

iOS JavaScriptCore 注入(UIWebView)

// 获取 WebView 中 JS上下文
JSContext *context = [webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//注入 callNative 函数方法
context[@"callNative"] = ^( JSValue * params )
{
    // 解析参数找到对应的方法进行处理
    // 同步回调
    NSInvocation *invocation = [执行方法]
    [invocation invoke];
    return invocation;
    // 异步回调
    return nil
}
复制代码

js 调用方式

callNative({
    action: 'bmImage/camera',
    params:{
        "imageNum":2,
        "allowCrop":true,
        "callbackId":"bmImage.camera1527235458519"
    }
});
复制代码

android webView 注入

// 经过 addJavascriptInterface 将对象映射到 JS 对象
// android 对象
// js 的对象名
mWebView.addJavascriptInterface(new JavaScriptBridge(), "callNative");
复制代码

js 调用方式

callNative.bmImage.camera({
    "imageNum":2,
    "allowCrop":true,
    "callbackId":"bmImage.camera1527235458519"
})
复制代码

这种方式就更接近写代码的方式了,无论是调用方式、传递参数的方式仍是同步异步回调都更好了。可是这种方式也有须要注意的地方,首先须要注意客户端注入的时机,在 loadUrl 以前注入是无效的,可是在 FinishLoad 以后注入可能 webView 已经调用了方法,此时会出现调用不到该方法,因此还须要其余的机制来保证这个问题。好比微信公众号的 wx.config 接口。

native 调用 js

前面已经讲了 js 调用 客户端,js 在向客户端发出调用指令以后,若是是同步方法,好比 获取当前客户端版本能当即获得结果,若是是异步调用,以前每次调用客户端的时候都传递了一个 callbackId,这个就是为客户端回调 js 作准备。

首先 js 会提供一个方法等待客户端调用

window.nativeCallbackMap = {};
const callJs = (data) => {
    var data = JSON.parse(data);
    var callback = window.nativeCallbackMap[data['callbackId']
    if(callback){
        callback.call(null, data['resData'])
    }
    delete window.nativeCallbackMap[data['callbackId']
}
复制代码

iOS 调用 js

NSString *resDataString = [self _serializeMessageData:data];
NSString* javascriptCommand = [NSString stringWithFormat:@"callJs('%@');", resDataString];
if ([[NSThread currentThread] isMainThread]) {
    [self.webView evaluateJavaScript:javascriptCommand completionHandler:nil];
} else {
    __strong typeof(self)strongSelf = self;
    dispatch_sync(dispatch_get_main_queue(), ^{
        [strongSelf.webView evaluateJavaScript:javascriptCommand completionHandler:nil];
    });
}
复制代码

android 调用 js

final int version = Build.VERSION.SDK_INT;
// 由于该方法在 Android 4.4 版本才可以使用,因此使用时需进行版本判断
if (version < 18) {
    mWebView.loadUrl("javascript:callJs()");
} else {
    mWebView.evaluateJavascript("javascript:callJs()", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            // js 的同步返回
        }
    });
}
复制代码

上述 js bridge 的实现大体就是如今大部分的客户端与 js 的通讯方式,如今 webView 也丰富了本身的功能,有能力调用客户端提供的方法,完成全部客户端能作的功能,js bridge 设计完以后剩下的就是需求推进,根据所需的功能提供不一样的方法供前端调用。

若是须要与原生页面间互相通讯,应该约定一种 scheme,也能够理解成一种 URI 的实现,经过 js bridge 的接口调用客户端(此时还能够传一个回调到客户端,当客户端完成某个操做以后返回该页面执行回调,好比日期选择),客户端解析协议,打开对应的原生页面,完成对应的操做。scheme 仍是手机浏览器会识别的一种协议,若是有对应的 APP 能够直接呼起 APP,可是当你打开客户端页面的参数太多时,你的 scheme 会变得超长,浏览器会将 URL 截断,这个时候还须要简单的短链服务转化

优势:

  • 首先具备全部 webView 全部的优势
  • 解决了单纯 webView 的交互功能受限,能调用原生接口完成需求

缺点

  • 也没法避免 webView 所带来的性能问题,打开白屏时间长,用户体验较差,等问题,也存在受限与浏览器,及重复打开须要重复加载等问题
  • 全部新须要客户端提供的功能都须要客户端发版
  • 随着业务发展客户端定义的接口必须向下兼容,这会带来不少冗余代码

表明产品:

  • 公众号使用到微信 sdk 的功能
  • AppCan、PhoneGap、cordova 等 hybrid 框架

小程序

在 hybrid 混合应用稳定以后,你们发现功能上已经能知足开发需求了,因此下个阶段应该追求体验和性能上的提高,就是 RN 的方案了,ReactNative 的方案已经逐渐被你们所熟知并逐步落到项目中,混合应用方案也是本文介绍的重点,后面会详述。

这里想先讲一讲小程序,由于在技术的演进上小程序才应该是下一小步的提高,而且小程序严格意义上来讲并不是 APP 的一种方案,更像是微信分发生态中的体验进化,在使用公众号这类 hybrid 的方案时,发现体验和交互上的一些问题,好比加载白屏时间,页面之间跳转的交互,页面内容的缓存等等。

因为小程序如今实现方案面向开发者也是一个黑盒,我也是一边了解一边猜小程序如何实现的,小程序的框架包含两部分:View 视图层和 APP service。前者用来渲染页面结构,后者用来作逻辑处理,接口调用。他们在两个进程里运行,有点相似在当前的页面里使用 web worker,关系以下图:

APP View

咱们写的视图层和逻辑代码是分离的,WXML 和 WXSS 构图了视图层的代码,WXML 经过 wcc 工具转换成 VDOM,WXSS 经过 wxsc 转化 style 标签。底层经过 WAWebview.js 来提供底层的封装,每个视图就会有一个 webView 来渲染,这也就提升了页面渲染性能的问题,多个视图页面时就会有多个 webView 进程,因此小程序对页面层级是有限制的,内存受限。视图层主要包括如下内容:

  • WeixinJSBridge 封装和上述的 hybrid 的 jsBridge 同样
  • 小程序提供的组件注册,一些能操做 DOM 的 API
  • 渲染的实现:VDOM、diff、render UI(在高人知道下了解到渲染出来的本质仍是 webview,在原生端只支持 flex 布局,可是小程序里还支持 web 的布局,由此能够看出,这里必定是 webView 的渲染,并不是原生)
  • 页面生命周期管理

APP Service

逻辑处理的代码所有加载到一个进程 APP service 中,和视图 webView 不一样,全部的代码逻辑都会一次性所有加载到这个进程中,由于主要的瓶颈仍是来自于渲染性能,而且全部的逻辑代码都加载到进程中保证视图切换的流程性,而且加上小程序有 2MB 大小的限制,在如今的网络环境下一次加载体验会更好。APP Service 包含如下内容:

  • WeixinJSBridge 封装和上述的 hybrid 的 jsBridge 同样
  • 全部小程序提供的 API 方法注入,全局方法注入
  • AMD 模块化实现

小程序的开发环境

小程序运行在开发环境中和线上环境是不一样的,线上环境 iOS 和 android 都有真正的 webView 环境提供,在开发过程当中小程序提供了一个 IDE,IDE 是基于 nwjs 实现的,钉钉客户端也是基于这个实现的。其实就是在 PC 端的客户端环境下尽量提供原生能力,若是是移动端才会有的差别化能力就会 mock 掉。两边环境不一样还体如今 APP Service 中,APP Service 主要是调用客户端底层,因此底层的不一样也影响着这层的封装。

APP View 与 APP service 通讯

当咱们理解了前面的 hybrid 的通讯原理,这里的通讯就比较好理解了。小程序的 bridge 实现原理就和 hybrid 同样了。iOS 和 android 就很少说了。基于 nwjs 的客户端是经过 window.postMessage 实现的,使用 chrome 扩展的接口注入一个 contentScript.js,封装了 postMessage 方法。

// 发送消息经过 
window.postMessage(data, ‘*’);
// 接受消息经过
window.addEventListener(‘message’, messageHandler);
复制代码

小程序的推进主要来自微信平台的大流量,这就是所谓的微信流量红利,公众号已经承载了传播和拉新的低成本方式,可是体验一直被诟病,限制于 hybrid 的体验问题让追求体验的开发者逐渐没法忍受,此时小程序原生的组件,良好多页面切换,几乎没有白屏时间的等待,让你们又有了探索无限的可能。

优势:

  • 首先具备全部 hybrid 全部的优势
  • 无需安装,极速打开
  • 原生的组件有了体验上质的提高
  • 性能上也有了新的飞跃,白屏时间,页面切换也有了更好的体验(为了减小白屏时间,小程序采用预加载的方案,而分不清哪一个会是逻辑里的下一个页面,因此小程序将全部 webView 全局预加载,这也是页面层级和包大小限制的主要缘由)

缺点

  • 只能用于微信平台,若是有多端的用户需求,开发成本增长了
  • 组件仍是较少,不少操做 DOM 的方式也会有所限制,完成需求受限
  • 为了性能的体验,包的大小受限,打开页面的层级受限

表明产品:

  • 各类客户端的小程序

快应用

快应用是对标微信小程序的一场阻截,几大手机厂商联合发布这个方案,一瞬间吸引了你们的眼球,愈来愈多端让前端追都追不过来(这么多技术确实让人很绝望,甚至前段时间你们去恶意灌水了不少大项目的 issue,但这也是前端蓬勃发展的表现,当这些前沿的技术落地在项目中甚至你只是完成一个 demo 的时候,你仍是会有抑制不住的兴奋和成就感)

快应用对自个人介绍是基于手机硬件平台的新型应用形态,用的是新型应用形态,并不是新技术,相似这样的实现有 PWA,只是 PWA 缺乏了手机硬件平台的支持。

图片来源:快应用发布会PPT

快应用标准是由手机厂商组成的快应用联盟联合制定,快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台。底层的硬件平台提供底层 API 让开发者调用,原生渲染组件,hybrid bridge 通讯,原理大体也与小程序实现相似,这里就不详细说明,下面是几张快应用发布会的 PPT 截图,若是理解了上面的原理,这个应该就是比较好理解了。

之因此说是小程序的一场阻截,小程序曾放话说,将来两年内,小程序将取代 80% 的 APP 市场,而手机厂商并不但愿大量的 APP 被取代,因此催生了快应用的落地。可是创建在几大国内手机厂商硬件平台上的方案,就目前来看支持性,通用性,社交性上都是比不上小程序的。二者的前景不敢妄加揣测,可是小程序凭借微信平台的用户量和用户粘性,比几大手机厂商实现的标准化仍是一目了然的。

优缺点就不详述了,一目了然

Flutter

ReactNative 的开疆扩土进一步奠基了,React 的霸主地位,React 也由一个框架演进成了,以 React 为基础的前端多端解决方案,技术栈的生态方案。google 怎么可能放弃本身在科技领域的地位,即便是前端相关的技术,因此出了 Flutter 技术方案完成 APP 的一环。

借由自己的 android 和 chrome 的 平台优点,准备重磅推出 Fuchsia OS,打造本身不基于 Linux 的底层系统,而 Fuchsia OS 钦定 UI Toolkit 就是 Flutter。Flutter 最大的改进就是本身重作了渲染引擎去渲染页面,将混合应用的渲染性能推向了另外一个高度。

Flutter 之于 RN 来说最大的区别在于渲染,脱离了 JSFramework 的 Component 递归传递和逐个计算绘制,更像是 canvas 绘制,将 UI 直接绘制出来,Flutter 都是在状态变动时从新构建 Widget Tree,Flutter 的渲染引擎在将 Widget Tree 转化成渲染的 Render Tree,最后交给操做系统调用 GPU 去渲染,Flutter 由 Dart 程序写的,Dart 和 native 之间仍然存在一个接口,能够进行数据编码和解码,这可能比 JavaScript bridge 快好几个数量级。

使用 Dart 是整个框架不太被前端所接受的问题,可是代码都是相通的,上手仍是很快,尝试 demo 期间仍是很快就能模仿出一个页面,语言上的争论就不说了,毕竟都不如 PHP。

因为本身使用 Flutter 还在 demo 阶段,并无真正围绕 Flutter 作过相关的解决方案,具体的细节实现也没有清楚的认知,也不适合展开详述,很早以前以矢量图起家的 Adobe 也尝试过相似的方案,可是最终没有把这条路走下去。

若是有兴趣能够移步:

weex 和 ReactNative

在讲这两种混合应用以前,再啰嗦几句,上面大体了解了各类混合应用的方案,这些方案了解其原理和使用场景,在合适的场景去使用对应的方案,不要为了落地某项方案而把业务生搬硬套,没有一种方案是绝对的好,在不一样的业务需求的驱动下权衡利弊再作决策。

这二者一直有人在试图去比较分析得出利弊,看哪一种更好,甚至有人偏激的认为 Weex 就不配合 RN 相提并论,可是我仍是想说理性的看待这个问题,客观的面对前端技术,曾经 React 和 Vue 也是这样的差距,直到如今依旧有人认为使用 Vue 低俗,使用 React 高雅,但雅俗之分真的如此吗?仍是应该理解其原理,辨别本身的业务场景,团队学习成本,开发体验等等一系列的缘由,而后再作评判。

就目前的各方面状况讲,RN 确实更优,横空出世,背靠当时火的不行 React,一路都是风光无限。这种解决方案出来以后,若是没有竞争者的挑战,没有对比,没有选择,也不必定是好事儿,虽然如今二者还有很大的差距。

我猜 Weex 始于 KPI,毕竟大厂造轮子来升级是很是好的途径之一,可是日后发展,却成了两大阵营完善生态的方案,如今基于 React 的一整套的前端解决方案覆盖PC、Web、Native 可谓是成了大公司前端方案的标准套路,后起之秀 Vue 用良好的开发体验和低门槛的入门方式开始抢占用户,各类前端培训学校让一些前端不懂 js 基础,可是 Vue 溜的飞起。但Vue 并无造成对 Native 的覆盖,这个可能也是 Weex 和 Vue 一拍即合的缘由,双方都有需求,就开始了合做。

扯会正题,在大前端逐渐融合的背景下,做为以 Vue 创建技术栈的团队开始寻找客户端的方案,作技术选型开始考虑业务达成、团队学习成本、开发效率,性能效率等问题,咱们一开始也是将 Weex 和 RN 做为比较,在上一家公司使用的是 RN,可是现团队创建技术栈基于 Vue,考虑到第二个因素偏向 Weex,实际调研过程当中发现 Weex 的坑确实不少,可是好在有不少问题已经有解决方案了,固然无论想要使用上述两种方案,都须要有必定的原生能力。实际开发过程当中发现随着对 Weex 的了解愈来愈多,开发效率也愈来愈快,入坑以后才发现了第三个和第四个甚至第五第六个优点。

至于 Weex 最大的卖点,兼容三端,Web 端这个优点每一个人都有本身的见解,咱们尝试了去兼容 Web 可是发现反而牺牲了开发效率,有兼容的时间,单独开发一套也出来了,而且更重要的是因为用户习惯的不一致,Web 端和 Native 针对的用户群体也不同,在这个流量就是金子的环境下牺牲用户体验和开发效率去兼容 Web 并不是是咱们的初衷。

固然也遇到了一些问题,一开始 Weex 没有托管 Apache 的时候还有 issue,不少问题确实不能及时获得解决,因此决定脱离 Weex 的全部 module,自行开发全部 module,从新设计,这也让咱们对项目有了一些控制。

决定了自行开发 module 和组件以后,减小了对 Weex 自己的依赖,项目自己受到的限制也愈来愈小,业务很快也完成了。固然如今相似 demo 跑不起来,毁灭式的升级,市场组件没法接入,bug 横飞,组件不足以完成业务功能,兼容性不足,热更新,公共文件致使包过大,社区经营很差等等这些问题,也都是一个技术产品成长的必经之路。

两种方案在选型的时候,必定有可取的地方才会选择,工程师的精神就是趟坑,找准正确的方向而后勇往直前,两种方案都有已经上线的优秀产品,说明是这条路至少是能走通的,固然咱们也在过程当中总结了咱们的解决方案,但愿能帮助在 Weex 路上举步维艰的同行人,无论选择哪一种方案,选择了就把他作好。Eros项目地址请戳(不要吝啬你的 star)Eros文档地址请戳。咱们公司已有三个项目经过 Eros 上线,也有几十个应用经过 Eros 发布上线。

固然还有不少优秀的方案没有一一枚举,好比:kotlin、WebAssembly,每一个方案都沉淀了不少前端工程师的积累,提供了各类能落地可实行的方案 phonegap -> cordova、mui、appcan、apicloud 等等

下一篇文章会详细的讲述 Weex 的原理

相关文章
相关标签/搜索