從 微信 JS-SDK 認識 JSBridge

作者: 熊的貓

https://juejin.cn/post/7199297355748458551

前言

前段時間由於要實現 H5 移動端拉取微信卡包並同步卡包數據的功能,於是在項目中引入了   微信 JS-SDK(jweixin)[1] 相關包實現功能,但也由此讓我對其產生了好奇心,於是打算好好了解下相關的內容,通過查閱相關資料發現這其實屬於 JSBridge 的一種實現方式。

因此,只要瞭解 JSBridge 就能明白 微信 JS-SDK 是怎麼一回事。

爲什麼需要 JSBridge?

相信大多數人都有相同的經歷,第一次瞭解到關於 JSBridge 都是從 微信 JS-SDK(WeiXinJSBridge) 開始,當然如果你從事的是 Hybrid 應用React-Native 開發的話相信你自然(應該、會)很瞭解。

其實 JSBridge 早就出現並被實際應用了,如早前桌面應用的消息推送等,而在移動端盛行的時代已經越來越需要 JSBridge,因爲我們期望移動端(Hybrid 應用React-Native)能做更多的事情,其中包括使用 客戶端原生功能 提供更好的 交互 和 服務 等。

然而 JavaScript 並不能直接調用和它不同語言(如 Java、C/C++ 等)提供的功能特性,因此需要一箇中間層去實現 JavaScript其他語言 間的一個相互協作,這裏通過一個 Node 架構來進行說明。

Node 架構

核心內容如下:

這裏不難發現 Node Bindings 就有點類似 JSBridge 的功能,所以 JSBridge 本身是一個很簡單的東西,其更多的是 一種形式、一種思想

爲什麼叫 JSBridge?

Stack Overflow 聯合創始人 Jeff Atwood 在 2007 年的博客《The Principle of Least Power[2]》中認爲 “任何可以使用 JavaScript 來編寫的應用,並最終也會由 JavaScript 編寫”,後來 JavaScript 的發展確實非常驚人,現在我們可以基於 JavaScript 來做各種事情,比如 網頁、APP、小程序、後端等,並且各種相關的生態越來越豐富。

作爲 Web 技術邏輯核心的 JavaScript 自然而然就需要承擔與 其他技術 進行『橋接』的職責,而且任何一個 移動操作系統 中都會包含 運行 JavaScript 的容器環境,例如 WebViewJSCore 等,這就意味着 運行 JavaScript 不用像運行其他語言時需要額外添加相應的運行環境。

JSBridge 應用在國內真正流行起來則是因爲 微信 的出現,當時微信的一個主要功能就是可以在網頁中通過 JSBridge 來實現 內容分享

JSBridge 能做什麼?

舉個最常見的前端和後端的例子,後端只提供了一個查找接口,但是沒有提供更新接口,那麼對於前端來講就是再想實現更新接口,也是沒有任何法子的!

同樣的,JSBridge 能做什麼得看原生端給 JavaScript 提供調用 Native 什麼功能的接口,比如通過 微信 JS-SDK 網頁開發者可藉助微信使用 拍照、選圖、語音、位置 等手機系統的能力,同時可以直接使用 微信分享、掃一掃、卡券、支付 等微信特有的能力。

JSBridge 作爲 JavaScriptNative 之間的一個 橋樑,表面上看是允許 JavaScript 調用 Native 的功能,但其核心是建立 Native非 Native 間消息 雙向通信 通道。

雙向通信的通道

JSBridge 是如何實現的?

JavaScript 的運行需要 JS 引擎的支持,包括 Chrome V8Firefox SpiderMonkeySafari JavaScriptCore 等,總之 JavaScript 運行環境 是和 原生運行環境 是天然隔離的,因此,在 JSBridge 的設計中我們可以把它 類比JSONP 的流程:

JSBridge 實現 JavaScript 調用的方式有兩種,如下:

在開始分析具體內容之前,還是有必要了解一下前置知識 WebView

WebView 是什麼?

WebView 是 原生系統 用於 移動端 APP 嵌入 Web 的技術,方式是內置了一款高性能 webkit 內核瀏覽器,一般會在 SDK 中封裝爲一個 WebView 組件。

WebView 具有一般 View 的屬性和設置外,還對 url 進行請求、頁面加載、渲染、頁面交互進行增強處理,提供更強大的功能。

WebView 的優勢 在於當需要 更新頁面佈局業務邏輯發生變更 時,能夠更便捷的提供 APP 更新:

微信小程序中的 WebView

小程序的主要開發語言是 JavaScript ,其中 邏輯層渲染層 是分開的,分別運行在不同的線程中,而其中的渲染層就是運行在 WebView 上:

5AqrTS

在開發過程中遇到的一個 坑點 就是

JavaScript 調用 Native — 實現方案一

通過 JavaScript 調用 Native 的方式,又會分爲:

【 注入 API 】

核心原理

這裏不通過 iOSUIWebViewWKWebView 注入方式來介紹了,感興趣可以自行查找資料,咱們這裏直接通過 微信 JS-SDK[3] 來看看。

當通過 <script src="https://res2.wx.qq.com/open/js/jweixin-1.4.0.js"></script> 的方式引入 JS-SDK 之後,就可以在頁面中使用和 微信相關的 API,例如:

// 微信授權
window.wx.config(wechatConfig)

// 授權回調
window.wx.ready(function () {...})

// 異常處理
window.wx.error(function (err) {...})

// 拉起微信卡包
window.wx.invoke('chooseInvoice', invokeConf, function (res) {...})

如果通過其內部編譯打包後的代碼(簡化版)來看的話,其實不難發現:

!(function (e, n) {
  'function' == typeof define && (define.amd || define.cmd)
    ? define(function () {
        return n(e)
      })
    : n(e, !0)
})(this, function (e, n) {
   ...
   function i(n, i, t) {
    e.WeixinJSBridge
      ? WeixinJSBridge.invoke(n, o(i)function (e) {
          c(n, e, t)
        })
      : u(n, t)
   }
   
   if (!e.jWeixin) {
    
    var N = {
        config(){
          i(...)
        },
        ready(){},
        error(){},
        ...
    }
    
    return (
      S.addEventListener(
        'error',callback1,
        !0
      ),
      S.addEventListener(
        'load',callback2,
        !0
      ),
      n && (e.wx = e.jWeixin = N),
      N
    )
  }
})

【 劫持 URL Scheme 】

URL Scheme 是什麼?

URL Scheme 是一種特殊的 URL,一般用於在 Web 端喚醒 App(或是跳轉到 App 的某個頁面),它能方便的實現 App 間互相調用(例如 QQ 和 微信 相互分享訊息)。

URL Scheme 的形式和 普通 URL(如:https://www.baidu.com)相似,主要區別是 protocolhost 一般是對應 APP 自定義的。

通常當 App 被安裝後會在系統上註冊一個 自定義的 URL Scheme,比如 weixin:// 這種,所以我們在手機瀏覽器裏面訪問這個 scheme 地址,系統就會喚起對應的 App

例如,當在瀏覽器中訪問 weixin:// 時,瀏覽器就會詢問你是否需要打開對應的 APP:

劫持原理

Web 端通過某種方式(如 iframe.src)發送 URL Scheme 請求,之後 Native 攔截到請求並根據 URL Scheme攜帶的參數 進行對應操作。

例如,對於谷歌瀏覽器可以通過 chrome://version/、chrome://chrome-urls/、chrome://settings/ 定位到不同的頁面內容,假設 跳轉到谷歌的設置頁並期望當前搜索引擎改爲百度,可以這樣設計 chrome://settings/engine?changeTo=baidu&callbak=callback_id:

以上只是一個假設哈,並不是說真的可以這樣去針對谷歌瀏覽器進行修改,當然它要是真的支持也不是不可以。

是不是感覺確實和 JSONP 的流程很相似呀 ~ ~

【 彈窗攔截 】

彈窗攔截核心:利用彈窗會觸發 WebView 相應事件來實現的

一般是在通過攔截 Prompt、Confirm、Alert 等方法,然後解析它們傳遞過來的消息,但這種方法存在的缺陷就是 iOS 中的 UIWebView 不支持,而且 iOS 中的 WKWebView 又有更好的 scriptMessageHandler,因此很難統一。

Native 調用 JavaScript — 實現方案二

Native 調用 JavaScript 的方式本質就是 執行拼接 JavaScript 字符串,這就好比我們通過 eval() 函數來執行 JavaScript 字符串形式的代碼一樣,不同的系統也有相應的方法執行 JavaScript 腳本。

Android

Android 中需要根據版本來區分:

IOS

IOS 中需要根據不同的 WebView 進行區分:

最後

以上通過 微信 JS-SDKJSBridge 的一個簡單介紹,大家現在應該不至於認爲 JSBridge 是一個高大上、深不可測的東西了,畢竟其核心思想是清晰明瞭的,而且本質上還是需要強依賴於原生端的具體實現。

參考資料

參考資料

[1] 微信 JS-SDK(jweixin): https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html

[2] The Principle of Least Power: https://link.zhihu.com/?target=https%3A//blog.codinghorror.com/the-principle-of-least-power/

[3] 微信 JS-SDK: https://res2.wx.qq.com/open/js/jweixin-1.4.0.js

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/yi5U8FjeJ9YKc1u4Bqp5Fg