共计 5739 个字符,预计需要花费 15 分钟才能阅读完成。
前言
前段时间因为要实现 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 间接调用
- 提供 http 模块、流模块、fs 文件模块等等,
-
中间层 Node Bindings
- 次要是使 JavaScript 和 C/C++ 进行通信,起因是 JavaScript 无奈间接调用 C/C++ 的库(libuv),须要一个两头的桥梁,node 中提供了很多 binding,这些称为
Node bindings
- 次要是使 JavaScript 和 C/C++ 进行通信,起因是 JavaScript 无奈间接调用 C/C++ 的库(libuv),须要一个两头的桥梁,node 中提供了很多 binding,这些称为
-
底层 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 的容器环境,例如 WebView
、JSCore
等,这就意味着 运行 JavaScript 不必像运行其余语言时须要额定增加相应的运行环境。
JSBridge
利用在国内真正流行起来则是因为 微信 的呈现,过后微信的一个次要性能就是能够在网页中通过JSBridge
来实现 内容分享。
JSBridge 能做什么?
举个最常见的前端和后端的例子,后端只提供了一个查找接口,然而没有提供更新接口,那么对于前端来讲就是再想实现更新接口,也是没有任何法子的!
同样的,JSBridge 能做什么得看原生端给 JavaScript 提供调用 Native 什么性能的接口,比方通过 微信 JS-SDK
网页开发者可借助微信应用 拍照、选图、语音、地位 等手机零碎的能力,同时能够间接应用 微信分享、扫一扫、卡券、领取 等微信特有的能力。
JSBridge
作为 JavaScript
与 Native
之间的一个 桥梁 ,外表上看是容许 JavaScript 调用 Native 的性能,但其外围是建设 Native 和 非 Native 间音讯 双向通信 通道。
双向通信的通道:
-
JavaScript 向 Native 发送音讯:
- 调用 Native 性能
- 告诉 Native 以后 JavaScript 的相干状态等
-
Native 向 JavaScript 发送音讯:
- 回溯调用后果
- 音讯推送
- 告诉 JavaScript 以后 Native 的状态等
JSBridge 是如何实现的?
JavaScript 的运行须要 JS 引擎的反对,包含 Chrome V8
、Firefox SpiderMonkey
、Safari 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
这里不通过 iOS
的 UIWebView
和 WKWebView
注入形式来介绍了,感兴趣能够自行查找材料,咱们这里间接通过 微信 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
)类似,次要区别是 protocol
和 host
个别是对应 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-SDK
到JSBridge
的一个简略介绍,大家当初应该不至于认为JSBridge
是一个高大上、深不可测的货色了,毕竟其核心思想是清晰明了的,而且实质上还是须要强依赖于原生端的具体实现。
心愿本文对你有所帮忙!!!
本文参加了 SegmentFault 思否写作挑战赛,欢送正在浏览的你也退出。