淺談Hybrid

引言

隨着 Web 技術和移動設備的飛速發展,各類 APP 層出不窮,極速的業務擴展提升了團隊對開發效率的要求,這個時候使用 IOS/Andriod 開發一個 APP 彷佛成本有點太高了,而 H5 的低成本、高效率、跨平臺等特性立刻被利用起來造成了一種新的開發模式:Hybrid APPjavascript

Hybrid 技術已經成爲一種最主流最多見的方案。一套好的 Hybrid 架構解決方案能讓 App 既能擁有極致的體驗和性能,同時也能擁有 Web 技術 靈活的開發模式、跨平臺能力以及熱更新機制。本文主要是結合我最近開發的一個 Hybrid 項目(https://github.com/Jack-cool/hybrid_jd),帶你們全面瞭解一下 Hybrid。前端

現有混合方案

深刻了解 Hybrid 前,讓咱們先來看一下目前市面上比較成熟的混合解決方案。java

基於 WebView UI 的基礎方案

這種是市面上大多數 app 採起的方案,也是混合開發最基礎的方案。在 webview 的基礎上,與原生客戶端創建js bridge橋接,以達到 js 調用Native API和 Native 執行js方法的目的。react

目前國內絕大部分的大廠都有一套本身的基於 webview ui 的 hybrid 解決方案,例如微信的JS-SDK,支付寶的JSAPI等,經過JSBridge完成 h5 與 Native 的雙向通信,從而賦予 H5 必定程度的原生能力。android

基於 Native UI 的方案

能夠簡單理解爲「跨平臺」,如今比較通用的有React NativeWeexFlutter等。在賦予 H5 原生 API 能力的基礎上,進一步經過 JSBridge 將 JS 解析成的虛擬節點數(Virtual DOM)傳遞到 Native 並使用原生渲染。咱們這裏來看下上面提到的這三種:ios

React Native

「Learn once, write anywhere」,React Native採用了 React 的設計模式,但 UI 渲染、動畫效果、網絡請求等均由原生端實現(因爲 JS 是單線程,不大可能處理太多耗時的操做)。開發者編寫的 JS 代碼,經過 React Native 的中間層轉化爲原生控件和操做,極大的提升了用戶體驗。git

React Native全部的標籤都不是真實控件,JS 代碼中所寫控件的做用,相似 Map 中的 key 值。JS 端經過這個 key 組合的 Dom ,最後 Native 端會解析這個 Dom ,獲得對應的 Native 控件渲染,如 Android 中 標籤對應 ViewGroup 控件。github

總結下來,就是:React Native 是利用 JS 來調用 Native 端的組件,從而實現相應的功能。web

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 封裝:

// 調用
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 進行數據的發送,客戶端進行攔截並獲取數據。

// 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 中拿到返回值。

//異步執行JS代碼,並獲取返回值
mWebView.evaluateJavascript("javascript: 方法名('參數,須要轉爲字符串')", new ValueCallback() {
        @Override
        public void onReceiveValue(String value) {
            //這裏的value即爲對應JS方法的返回值
        }
});

stringByEvaluatingJavaScriptFromString

在 iOS 中 Native 經過stringByEvaluatingJavaScriptFromString調用 Html 綁定在 window 上的函數。

// Swift
webview.stringByEvaluatingJavaScriptFromString("方法名('參數')")
// oc
[webView stringByEvaluatingJavaScriptFromString:@"方法名(參數);"];

總結

看完本篇文章,相信你對 Hybrid 有了一個初步的瞭解。雖然本篇比較基礎,可是隻有了解了最本質的底層原理後,才能對現有的解決方案有一個很好的理解,你也能夠去打造適合你和團隊的Hybrid方案。固然了,後面會有對於 Hybrid 更深刻的探討,敬請期待哦!!

最後

你能夠關注個人同名公衆號【前端森林】,這裏我會按期發一些大前端相關的前沿文章和平常開發過程當中的實戰總結。固然,我也是開源社區的積極貢獻者,github地址https://github.com/Jack-cool,歡迎star!!!

相關文章
相關標籤/搜索