前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈Hybrid

浅谈Hybrid

作者头像
前端森林
发布2020-04-23 11:33:45
6.7K0
发布2020-04-23 11:33:45
举报
文章被收录于专栏:前端森林前端森林

引言

随着 Web 技术和移动设备的飞速发展,各种 APP 层出不穷,极速的业务扩展提高了团队对开发效率的要求,这个时候使用 IOS/Andriod 开发一个 APP 似乎成本有点过高了,而 H5 的低成本、高效率、跨平台等特性马上被利用起来形成了一种新的开发模式:Hybrid APP

Hybrid 技术已经成为一种最主流最常见的方案。一套好的 Hybrid 架构解决方案能让 App 既能拥有极致的体验和性能,同时也能拥有 Web 技术 灵活的开发模式、跨平台能力以及热更新机制。本文主要是结合我最近开发的一个 Hybrid 项目(https://github.com/Jack-cool/hybrid_jd),带大家全面了解一下 Hybrid。

现有混合方案

深入了解 Hybrid 前,让我们先来看一下目前市面上比较成熟的混合解决方案。

基于 WebView UI 的基础方案

这种是市面上大多数 app 采取的方案,也是混合开发最基础的方案。在 webview 的基础上,与原生客户端建立js bridge桥接,以达到 js 调用Native API和 Native 执行js方法的目的。

目前国内绝大部分的大厂都有一套自己的基于 webview ui 的 hybrid 解决方案,例如微信的JS-SDK,支付宝的JSAPI等,通过JSBridge完成 h5 与 Native 的双向通讯,从而赋予 H5 一定程度的原生能力。

基于 Native UI 的方案

可以简单理解为“跨平台”,现在比较通用的有React NativeWeexFlutter等。在赋予 H5 原生 API 能力的基础上,进一步通过 JSBridge 将 JS 解析成的虚拟节点数(Virtual DOM)传递到 Native 并使用原生渲染。我们这里来看下上面提到的这三种:

React Native

“Learn once, write anywhere”,React Native采用了 React 的设计模式,但 UI 渲染、动画效果、网络请求等均由原生端实现(由于 JS 是单线程,不大可能处理太多耗时的操作)。开发者编写的 JS 代码,通过 React Native 的中间层转化为原生控件和操作,极大的提高了用户体验。

React Native所有的标签都不是真实控件,JS 代码中所写控件的作用,类似 Map 中的 key 值。JS 端通过这个 key 组合的 Dom ,最后 Native 端会解析这个 Dom ,得到对应的 Native 控件渲染,如 Android 中 标签对应 ViewGroup 控件。

总结下来,就是:React Native 是利用 JS 来调用 Native 端的组件,从而实现相应的功能。

Weex

“Write once, run everywhere”,基于 Vue 设计模式,支持 web、android、ios 三端,原生端同样通过中间层转化,将控件和操作转化为原生逻辑来提升用户体验。

在 weex 中,主要包括三大部分:JS BridgeRenderDom,JS Bridge 主要用来和 JS 端实现进行双向通信,比如把 JS 端的 dom 结构传递给 Dom 线程。Dom 主要是用于负责 dom 的解析、映射、添加等等的操作,最后通知 UI 线程更新。而 Render 负责在 UI 线程中对 dom 实现渲染。

和 react native 一样,weex 所有的标签也都不是真实控件,JS 代码中所生成的 dom,最终都是由 Native 端解析,再得到对应的 Native 控件渲染,如 Android 中 标签对应 WXTextView 控件。

Flutter

Flutter 是谷歌 2018 年发布的跨平台移动 UI 框架。与 react native 和 weex 的通过 Javascript 开发不同,Flutter 的编程语言是Dart,所以执行时并不需要 Javascript 引擎,但实际效果最终也通过原生渲染。

看完这三种方案的简介,下面让我们简单来做个对比吧:

React Native

Weex

Flutter

平台实现

JavaScript

JavaScript

原生编码

引擎

JS V8

JSCore

Flutter engine

核心语言

React

Vue

Dart

框架程度

较重

较轻

特点

适合开发整体 App

适合单页面

适合开发整体 App

支持

Android、IOS

Android、IOS、Web

Android、IOS(可能还不止)

Apk 大小(Release)

7.6M

10.6M

8.1M

小程序

小程序开发本质上还是前端 HTML + CSS + JS 那一套逻辑,它基于 WebView 和微信(当然支付宝、百度、字节等现在都有自己的小程序,这里只是拿微信小程序做个说明)自己定义的一套 JS/WXML/WXSS/JSON 来开发和渲染页面。微信官方文档里提到,小程序运行在三端:iOS、Android 和用于调试的开发者工具,三端的脚本执行环境以及用于渲染非原生组件的环境是各不相同的。

通过更加定制化的 JSBridge,并使用双 WebView 双线程的模式隔离了 JS 逻辑与 UI 渲染,形成了特殊的开发模式,加强了 H5 与 Native 混合程度,提高了页面性能及开发体验。

PWA

Progressive Web App, 简称 PWA,是提升 Web App 体验的一种新方法,能给用户带来原生应用的体验。

PWA 能做到原生应用的体验不是靠某一项特定的技术,而是经过应用一系列新技术进行改进,在安全、性能和体验三个方面都有了很大的提升,PWA 本质上还是 Web App,并兼具了 Native App 的一些特性和优点,主要包括下面三点:

  • 可靠 - 即使在不稳定的网络环境下,也能快速加载并展现
  • 体验 - 快速响应,并且有平滑的动画响应用户的操作
  • 粘性 - 设备上的原生应用,具有沉浸式的用户体验,用户可以添加到桌面

Android 和主流的浏览器都早已支持了 PWA 标准,在 iOS 11.3 和 macOS 10.13.4 上,苹果的 Safari 上也支持了 PWA。相信在不久的将来势必会迎来 PWA 的大爆发...

看完目前主流的混合解决方案,我们回归本篇主题,讲解一下成熟解决方案背后的 Hybrid底层基础,要知道决定上层建筑的永远都是底层基础,新的技术层出不穷,只有原理是不变的~~

Hybrid 是什么,为什么要用 Hybrid?

Hybrid,字面意思“混合”。可以简单理解为是前端和客户端的混合开发。

让我们先来看一下目前主流的移动应用开发方式:

Native APP

Native App 是一种基于智能手机本地操作系统如 iOS、Android、WP 并使用原生程式编写运行的第三方应用程序,也叫本地 app。一般使用的开发语言为 Java、C++、Objective-C。。分别来看一下 Native 开发的优缺点:

  • 优点
    • 用户体验近乎完美
    • 性能稳定
    • 访问本地资源(通讯录、相册)
    • 操作流畅
    • 设计出色的动效、转场
    • 系统级的贴心通知或提醒
    • 用户留存率高
  • 缺点
    • 门槛高,原生开发人才稀缺,至少比前端和后端少,开发环境昂贵
    • 发布成本高,需要通过 store 或 market 的审核,导致更新缓慢
    • 维持多个版本、多个系统的成本比较高,而且必须做兼容
    • 无法跨平台,开发的成本比较大,各个系统独立开发

Web APP

Web App,顾名思义是指基于 Web 的应用,基本采用 Html5 语言写出,不需要下载安装。类似于现在所说的轻应用。基于浏览器运行的应用,基本上可以说是触屏版的网页应用。分别来看一下 Web 开发的优缺点:

  • 优点
    • 开发成本低
    • 临时入口,可以随意嵌入
    • 无需安装,不会占用手机内存,而且更新速度最快
    • 能够跨多个平台和终端
    • 不存在多版本问题,维护成本低
  • 缺点
    • 无法获取系统级别的通知,提醒,动效等等
    • 设计受限制较多
    • 体验较差
    • 受限于手机和浏览器性能,用户体验相较于其他模式最差
    • 用户留存率低

究其原因就是性能要求的问题。Web app 之所以能够占领开发市场,主要是因为它的开发速度快,使用简单,应用范围广,但是在性能方面因为无法调用全部硬件底层功能,就现在讲,还是比不过原生 App 的性能。

Hybrid APP

混合开发,也就是半原生半 Web 的开发模式,由原生提供统一的 API 给 JS 调用,实际的主要逻辑有 Html 和 JS 来完成,最终是放在 webview 中显示的,所以只需要写一套代码即可达到跨平台效果。

Hybrid App 兼具了 Native APP 用户体验佳、系统功能强大和 Web APP 跨平台、更新速度快的优势。本质其实是在原生的 App 中,使用 WebView 作为容器直接承载 Web 页面。因此,最核心的点就是 Native 端 与 H5 端 之间的双向通讯层,也就是我们常说的 JSBridge

下面让我们来看下 JS 与 Native(客户端)通信的方式吧。

JS 与客户端通信

JS 通知客户端(Native)

JS上下文注入

原理其实就是 Native 获取 JavaScript 环境上下文,并直接在上面挂载对象或者方法,使 JS 可以直接调用。

Android 与 IOS 分别拥有对应的挂载方式。分别对应是:苹果UIWebview JavaScriptCore注入安卓addJavascriptInterface注入苹果WKWebView scriptMessageHandler注入

上面这三种方式都可以被称为是JS上下文注入,他们都有一个共同的特点就是,不通过任何拦截的办法,而是直接将一个 native 对象(or 函数)注入到 JS 里面,可以由 Web 的 JS 代码直接调用,直接操作。

弹窗拦截

这种方式主要是通过修改浏览器 Window 对象的某些方法,然后拦截固定规则的参数,之后分发给客户端对应的处理方法,从而实现通信。

常用的四个方法:

  • alert: 可以被 webview 的 onJsAlert 监听
  • confirm: 可以被 webview 的 onJsConfirm 监听
  • prompt: 可以被 webview 的 onJsPrompt 监听

简单拿 prompt 来举例说明,Web 页面通过调用 prompt()方法,安卓客户端通过监听onJsPrompt事件,拦截传入的参数,如果参数符合一定协议规范,那么就解析参数,扔给后续的 Java 去处理。这种协议规范,最好是跟 iOS 的协议规范一样,这样跨端调起协议是一致的,但具体实现不一样而已。比如:jack://utils/{action}?a=a 这样的协议,而其他格式的 prompt 参数,是不会监听的,即除了 jack://utils/{action}?a=a 这样的规范协议,prompt 还是原来的 prompt。

但这几种方法在实际的使用中有利有弊,但由于prompt是几个里面唯一可以自定义返回值,可以做同步交互的,所以在目前的使用中,prompt是使用的最多的。

URL Schema

schema 是 URI 的一种格式,上文提到的jack://utils/${action}?a=a 就是一个 scheme 协议,这里说的 scheme(或者 schema)泛指安卓和 iOS 的 schema 协议,因为它比较通用。

安卓和 iOS 都可以通过拦截跳转页 URL 请求,然后解析这个 scheme 协议,符合约定规则的就给到对应的 Native 方法去处理。

安卓和 iOS 分别用于拦截 URL 请求的方法是:

  • android:shouldOverrideUrlLoading方法
  • iOS:UIWebView 的delegate函数

这里简单看一个之前项目中对于 schema 封装:

代码语言:javascript
复制
// 调用
window.fsInvoke.share({title: 'xxx', content: 'xxx'}, result => {
    if (result.errno === 0) {
        alert('分享成功')
    } else {
        // 分享失败
        alert(result.message)
    }
)

---------------------------下方为对fsInvoke的封装

(function(window, undefined) {
    // 分享
    invokeShare = (data, callback) => {
        _invoke('share', data, callback)
    }

    // 登录
    invokeLogin = (data, callback) => {
        _invoke('login', data, callback)
    }

    // 打开扫一扫
    invokeScan = (data, callback) => {
        _invoke('scan', data, callback)
    }

    _invoke = (action, data, callback) => {
        // 拼接schema协议
        let schema = `jack://utils/${action}?a=a`;
        Object.keys(data).forEach(key => {
            schema += `&${key}=${data[key]}`
        })

        // 处理callback
        let callbackName = '';
        if(typeof callback === 'string) {
            callbackName = callback
        } else {
            callbackName = action + Date.now();
            window[callbackName] = callback;
        }

        schema += `&callback=${callbackName}`

        // 触发
        let iframe = document.createElement('iframe');
        iframe.style.display = 'none';
        iframe.src = schema;
        let body = document.body;
        body.appendChild(iframe);
        setTimeout(function() {
            body.removeChild(iframe);
            iframe = null;
        })
    }

    // 暴露给全局
    window.fsInvoke = {
        share: invokeShare,
        login: invokeLogin,
        scan: invokeScan
    }
})(window)

说完了 JS 主动通知客户端(Native)的方式,下面让我们来看下客户端(Native)主动通知调用 JS。

客户端(Native)通知 JS

loadUrl

在安卓 4.4 以前是没有 evaluatingJavaScript API 的,只能通过 loadUrl 来调用 JS 方法,只能让某个 JS 方法执行,但是无法获取该方法的返回值。这时我们需要使用前面提到的 prompt 方法进行兼容,让 H5 端 通过 prompt 进行数据的发送,客户端进行拦截并获取数据。

代码语言:javascript
复制
// mWebView = new WebView(this); //即当前webview对象
mWebView.loadUrl("javascript: 方法名('参数,需要转为字符串')");

//ui线程中运行
 runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mWebView.loadUrl("javascript: 方法名('参数,需要转为字符串')");
            Toast.makeText(Activity名.this, "调用方法...", Toast.LENGTH_SHORT).show();
        }
});
evaluatingJavaScript

在安卓 4.4 之后,evaluatingJavaScript 是一个非常普遍的调用方式。通过 evaluateJavascript 异步调用 JS 方法,并且能在 onReceiveValue 中拿到返回值。

代码语言:javascript
复制
//异步执行JS代码,并获取返回值
mWebView.evaluateJavascript("javascript: 方法名('参数,需要转为字符串')", new ValueCallback() {
        @Override
        public void onReceiveValue(String value) {
    		//这里的value即为对应JS方法的返回值
        }
});
stringByEvaluatingJavaScriptFromString

在 iOS 中 Native 通过stringByEvaluatingJavaScriptFromString调用 Html 绑定在 window 上的函数。

代码语言:javascript
复制
// Swift
webview.stringByEvaluatingJavaScriptFromString("方法名('参数')")
// oc
[webView stringByEvaluatingJavaScriptFromString:@"方法名(参数);"];

总结

看完本篇文章,相信你对 Hybrid 有了一个初步的了解。虽然本篇比较基础,但是只有了解了最本质的底层原理后,才能对现有的解决方案有一个很好的理解,你也可以去打造适合你和团队的Hybrid方案。当然了,后面会有对于Hybrid更深入的探讨,敬请期待哦

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端森林 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 现有混合方案
    • 基于 WebView UI 的基础方案
      • 基于 Native UI 的方案
        • React Native
        • Weex
        • Flutter
      • 小程序
        • PWA
        • Hybrid 是什么,为什么要用 Hybrid?
          • Native APP
            • Web APP
              • Hybrid APP
              • JS 与客户端通信
                • JS 通知客户端(Native)
                  • JS上下文注入
                  • 弹窗拦截
                  • URL Schema
                • 客户端(Native)通知 JS
                  • loadUrl
                  • evaluatingJavaScript
                  • stringByEvaluatingJavaScriptFromString
              • 总结
              相关产品与服务
              云开发 CloudBase
              云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档