乐趣区

关于javascript:从-微信-JSSDK-认识-JSBridge

前言

前段时间因为要实现 H5 挪动端拉取微信卡包并同步卡包数据的性能,于是在我的项目中引入了 微信 JS-SDK(jweixin) 相干包实现性能,但也由此让我对其产生了好奇心,于是打算好好理解下相干的内容,通过查阅相干材料发现这其实属于 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 Api

    • 提供 http 模块、流模块、fs 文件模块等等,能够通过 JavaScript 间接调用
  • 中间层 Node Bindings

    • 次要是使 JavaScript 和 C/C++ 进行通信,起因是 JavaScript 无奈间接调用 C/C++ 的库(libuv),须要一个两头的桥梁,node 中提供了很多 binding,这些称为 Node bindings
  • 底层 V8 + libuv

    • v8 负责解释、执行顶层的 JavaScript 代码
    • libuv 负责提供 I/O 相干的操作,其次要语言是 C/C++ 语言,其目标就是实现一个 跨平台(如 Windows、Linux 等)的异步 I/O 库,它间接与操作系统进行交互

这里不难发现 Node Bindings 就有点相似 JSBridge 的性能,所以 JSBridge 自身是一个很简略的货色,其更多的是 一种模式、一种思维

为什么叫 JSBridge?

Stack Overflow 联结创始人 Jeff Atwood 在 2007 年的博客《The Principle of Least Power》中认为 “任何能够应用 JavaScript 来编写的利用,并最终也会由 JavaScript 编写”,起初 JavaScript 的倒退的确十分惊人,当初咱们能够基于 JavaScript 来做各种事件,比方 网页、APP、小程序、后端等,并且各种相干的生态越来越丰盛。

作为 Web 技术逻辑外围的 JavaScript 自然而然就须要承当与 其余技术 进行『 桥接 』的职责,而且任何一个 挪动操作系统 中都会蕴含 运行 JavaScript 的容器环境,例如 WebViewJSCore 等,这就意味着 运行 JavaScript 不必像运行其余语言时须要额定增加相应的运行环境。

JSBridge 利用在国内真正流行起来则是因为 微信 的呈现,过后微信的一个次要性能就是能够在网页中通过 JSBridge 来实现 内容分享

JSBridge 能做什么?

举个最常见的前端和后端的例子,后端只提供了一个查找接口,然而没有提供更新接口,那么对于前端来讲就是再想实现更新接口,也是没有任何法子的!

同样的,JSBridge 能做什么得看原生端给 JavaScript 提供调用 Native 什么性能的接口,比方通过 微信 JS-SDK 网页开发者可借助微信应用 拍照、选图、语音、地位 等手机零碎的能力,同时能够间接应用 微信分享、扫一扫、卡券、领取 等微信特有的能力。

JSBridge 作为 JavaScriptNative 之间的一个 桥梁 ,外表上看是容许 JavaScript 调用 Native 的性能,但其外围是建设 Native 非 Native 间音讯 双向通信 通道。

双向通信的通道

  • JavaScript 向 Native 发送音讯:

    • 调用 Native 性能
    • 告诉 Native 以后 JavaScript 的相干状态等
  • Native 向 JavaScript 发送音讯:

    • 回溯调用后果
    • 音讯推送
    • 告诉 JavaScript 以后 Native 的状态等

JSBridge 是如何实现的?

JavaScript 的运行须要 JS 引擎的反对,包含 Chrome V8Firefox SpiderMonkeySafari JavaScriptCore 等,总之 JavaScript 运行环境 是和 原生运行环境 是人造隔离的,因而,在 JSBridge 的设计中咱们能够把它 类比JSONP 的流程:

  • 客户端通过 JavaScript 定义一个回调函数,如: function callback(res) {...},并把这个回调函数的名称以参数的模式发送给服务端
  • 服务端获取到 callback 并携带对应的返回数据,以 JS 脚本模式返回给客户端
  • 客户端接管并执行对应的 JS 脚本即可

JSBridge 实现 JavaScript 调用的形式有两种,如下:

  • JavaScript 调用 Native
  • Native 调用 JavaScript

在开始剖析具体内容之前,还是有必要理解一下前置常识 WebView

WebView 是什么?

WebView 是 原生零碎 用于 挪动端 APP 嵌入 Web 的技术,形式是内置了一款高性能 webkit 内核浏览器,个别会在 SDK 中封装为一个 WebView 组件。

WebView 具备个别 View 的属性和设置外,还对 url 进行申请、页面加载、渲染、页面交互进行加强解决,提供更弱小的性能。

WebView 的劣势 在于当须要 更新页面布局 业务逻辑产生变更 时,可能更便捷的提供 APP 更新:

  • 对于 WebView 而言只须要批改前端局部的 Html、Css、JavaScript 等,告诉用户端进行刷新即可
  • 对于 Native 而言须要批改前端内容后,再进行打包降级,从新公布,告诉用户下载更新,装置后才能够应用最新的内容

微信小程序中的 WebView

小程序的次要开发语言是 JavaScript,其中 逻辑层 渲染层 是离开的,别离运行在不同的线程中,而其中的渲染层就是运行在 WebView 上:

运行环境 逻辑层 渲染层
iOS JavaScriptCore WKWebView
安卓 V8 chromium 定制内核
小程序开发者工具 NWJS Chrome WebView

在开发过程中遇到的一个 坑点 就是

  • 在真机中,须要实现同一域名下不同子门路的利用实现数据交互(纯前端操作,不波及接口),因为同域名且是基于同一个页面进行跳转的(当然只是看起来是),而且这个数据是 长期数据,因而感觉应用 sessionStorage 实现数据交互是很适合的
  • 实际上从 A 利用 跳转到 B 利用 中却无奈获取对应的数据,而这是因为 sessionStorage 是基于以后窗口的会话级的数据存储, 挪动端浏览器  或  微信内置浏览器 中在跳转新页面时,可能关上的是一个新的 WebView,这就相当于咱们在浏览器中的一个新窗口中进行存储,因而是没方法读取在之前的窗口中存储的数据

JavaScript 调用 Native — 实现计划一

通过 JavaScript 调用 Native 的形式,又会分为:

  • 注入 API
  • 劫持 URL Scheme
  • 弹窗拦挡

【注入 API】

外围原理

  • 通过 WebView 提供的接口,向 JavaScript 的上下文(window)中注入 对象 或者 办法
  • 容许 JavaScript 进行调用时,间接执行相应的 Native 代码逻辑,实现 JavaScript 调用 Native

这里不通过 iOSUIWebViewWKWebView 注入形式来介绍了,感兴趣能够自行查找材料,咱们这里间接通过 微信 JS-SDK 来看看。

当通过 <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) {...})

如果通过其外部编译打包后的代码(简化版)来看的话,其实不难发现:

  • 其中的 this(即参数 e)此时就是指向全局的 window 对象
  • 在代码中应用的 window.wx 实际上是 e.jWeixin 也是其中定义的 N 对象
  • 而在 N 对象中定义的各种办法实际上又是通过 e.WeixinJSBridge 上的办法来理论执行的
  • e.WeixinJSBridge 就是由 微信内置浏览器 向 window 对象中注入 WeiXinJsBridge 接口实现的

    !(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:

  • 谷歌客户端能够拦挡这个申请,去解析对应参数 changeTo 来批改默认引擎
  • 而后通过 WebView 下面的 callbacks 对象来依据 callback_id 进行回调

以上只是一个假如哈,并不是说真的能够这样去针对谷歌浏览器进行批改,当然它要是真的反对也不是不能够。

是不是感觉的确和 JSONP 的流程很类似呀 ~ ~

【弹窗拦挡】

弹窗拦挡外围:利用弹窗会触发 WebView 相应事件来实现的

个别是在通过拦挡 Prompt、Confirm、Alert 等办法,而后解析它们传递过去的音讯,但这种办法存在的缺点就是 iOS 中的 UIWebView 不反对,而且 iOS 中的 WKWebView 又有更好的 scriptMessageHandler,因而很难对立。

Native 调用 JavaScript — 实现计划二

Native 调用 JavaScript 的形式实质就是 执行拼接 JavaScript 字符串,这就好比咱们通过 eval() 函数来执行 JavaScript 字符串模式的代码一样,不同的零碎也有相应的办法执行 JavaScript 脚本。

Android

Android 中须要依据版本来辨别:

  • 安卓 4.4 之前的版本应用 loadUrl()

    • loadUrl() 不能获取 JavaScript 执行后的后果,这种形式更像在 <a href="javascript:void(0)">href 属性中编写的 JavaScript 代码

      webView.loadUrl("javascript:foo()")
  • 安卓 4.4 以上版本应用 evaluateJavascript()

    webView.evaluateJavascript("javascript:foo()", null);

    IOS

    IOS 中须要依据不同的 WebView 进行辨别:

  • UIWebView 中通常应用 stringByEvaluatingJavaScriptFromString

    results = [self.webView stringByEvaluatingJavaScriptFromString:"foo()"];
  • WKWebView 中通常应用 evaluateJavaScript

    [self.webView evaluateJavaScript:@"document.body.offsetHeight;" completionHandler:^(id _Nullable response, NSError * _Nullable error) {// 获取返回值}];

    最初

    以上通过 微信 JS-SDKJSBridge 的一个简略介绍,大家当初应该不至于认为 JSBridge 是一个高大上、深不可测的货色了,毕竟其核心思想是清晰明了的,而且实质上还是须要强依赖于原生端的具体实现。

心愿本文对你有所帮忙!!!

本文参加了 SegmentFault 思否写作挑战赛,欢送正在浏览的你也退出。

退出移动版