拖稿了很久的「Hybrid APP开发系列」又更新了~
今天继续写JSSDKjavascript
我以前文章介绍了经过 JSBridge 实现页面和NA的相互调用,而且介绍了模板本地包的开发和后台维护系统。今天介绍的是JSSDK,经过 JSSDK 能够实现:前端
抹平JSBridge的平台实现差别java
对齐端能力,内部消化版本差别node
sdk封装后的代码更加符合前端习惯ios
权限控制、鉴权、对外开放,实现生态建设web
关于sdk的代码级别的设计,能够参考文章:《JSSDK设计指南》chrome
若是作过微信页面开发的,应该都知道 wx.js
,这就是微信的JSSDK,在微信内须要调用微信的端能力就须要引入这个js。安全
JSSDK的设计包括两部分:微信
随着每一个NA客户端版本内置的js,称为: inject.js
,他的主要做用是封装JSBridge逻辑,经过随版更新实现减小端能力的版本分裂,下降整个sdk的代码复杂性。 inject.js
是一段js代码,当客户端加载一个页面的时候,由客户端在适当的时机注入到webview内执行,执行后的代码就会有给webview增长js方法,例如微信的 _WeixinJSBridge
,类比chrome开发插件当中的 content_scripts
,能够在 document_start
、 document_end
等时机进行执行。app
云端JS,即实际暴漏给开发者使用的js,称为: jssdk.js
,这个是真正开发者使用的sdk文件,经过 script
外链引入,例如 wx.js
,这个js文件经过和 inject.js
进行交换,完成端能力的调用、鉴权和客户端事件监听等操做
inject.js
是客户端和 jssdk
的「翻译官」,他接受页面 jssdk
的方法调用,将调用的命令解析成客户端能够理解的「语言」(JSBridge)而后传给客户端,同时当客户端有事件/回调响应的时候,也经过 inject.js
进行分发/回调。
例如,客户端实现了一个分享面板的UI组件,开放给web页面能够调用,这时候须要调用NA的端能力,JS须要将分享的:icon_url、title、content、link、type甚至订制的NA面板信息等传给NA,NA开始弹出这个面板,用户进入NA层进行交互;
当用户分享成功、失败、取消等事件发生的时候,须要回调JS代码,用户由回到了web页面,NA回调了JS callback,JS实现后续的逻辑。
jsbridge为:
demoapp://share/dialog?title=三水清&link=http://js8.in&
复制代码icon_url=xxx&content=我发现一个颇有用的前端公众号复制代码
inject.js
代码封装以下:
;(function (window, document) {复制代码function invoke (module, action, args, callback) {复制代码let scheme = `demoapp://${module}/${action}?`复制代码if (isFunction(args)) {复制代码callback = args复制代码args = null复制代码}复制代码// 处理下参数复制代码if (isString(args)) {复制代码scheme += args复制代码} else if (isObject(args)) {复制代码each(args, (k, v) => {复制代码if (isObject(v) || isArray(v)) {复制代码v = JSON.stringify(v)复制代码}复制代码scheme += `${k}=${v}`复制代码})复制代码}复制代码// callback独立传,方便全局函数名命名复制代码if (isFunction(callback)) {复制代码var funcName = '_jsbridge_cb_' + getId()复制代码window[funcName] = function () {复制代码callback.apply(window, ([]).slice.call(arguments, 0))复制代码}复制代码scheme += (!~scheme.indexOf('?') ? '&' : '?') + `callback=${funcName}`复制代码}复制代码if (os.ios && versionCompare(os.version, '9.0') >= 0) {复制代码window.location.href = scheme复制代码} else {复制代码var $node = document.createElement('iframe')复制代码$node.style.display = 'none'复制代码$node.src = scheme复制代码var body = document.body || document.getElementsByTagName('body')[0]复制代码body.appendChild($node)复制代码setTimeout(function () {复制代码body.removeChild($node)复制代码$node = null复制代码}, 10)复制代码}复制代码}复制代码var $ = {复制代码share: function (opts, callback) {复制代码var defaultOpts = {复制代码url: location.href,复制代码title: '三水清',复制代码content: '最好的前端公众号',复制代码icon_url: 'http://baidu.com/icon.png'复制代码}复制代码opts = Object.assign(defaultOpts, opts)复制代码invoke('share', 'dialog', opts, callback)复制代码}复制代码}复制代码window._InjectJS_ = $复制代码}(window, document))复制代码
jssdk.in
代码示例:
window.jssdk = {复制代码share: _InjectJS_.share复制代码}复制代码
页面调用:
jssdk.share({url: 'http://js8.in'}, (err, data) => {复制代码if (!err) {复制代码if (data.errno === 1) {复制代码alert('失败')复制代码}else if (data.errno === 2) {复制代码alert('取消')复制代码}else {复制代码alert(data.media) // 分享的平台id,好比webxin_timeline复制代码}复制代码}复制代码})复制代码
这样 jssdk
调用 inject.js
写法,看似画蛇添足,实则很巧妙,试想一下下面的场景:
客户端某个版本分享能力升级,须要作兼容
某版本分享能力有bug,会引发crash,不能在此版本调用
分享成功以后的回调须要作鉴权,防止恶意刷分享行为
JSBridge有scheme调起换成jsinterface的调起(参考本系列JSBridge文章)
若是这些代码都写在 jssdk.js
,那么随着版本的积累,代码会愈来愈臃肿,而且全部版本的端能力都集中在 jssdk.js
,很不利于管理,历史的包袱也甩不掉。
由于 inject.js
的设计机制,因此咱们但愿 inject.js
可以越早注入越好,这样咱们在页面head使用 jssdk.js
就不会找不到对象了!
咱们知道安卓WebView中能够经过 webview.loadUrl("javascript:xxx”)
的方式来调用js里面的代码,那么,咱们也能够利用 webview.loadUrl("javascript:xxx”);
的方式来加载注入一段 js 代码 。
安卓WebView 须要经过 webView.setWebViewClient(new MyWebClient());
的方式来监听网页加载的各个周期方法回调,那么咱们只须要在 onPageFinished(WebView view, String url)
中注入提早设置好的js 便可
在iOS中也有对应的时间点: webViewDidFinishLoad
和 didCreateJavaScriptContext
咱们可以找到的注入时机有限,为了保证jssdk代码在调用的时候,已经注入成功 inject.js
,咱们只能实现相似 DOMContentLoaded
这样的 ready
方法回调,使用jssdk的时候,所有写在 jssdk.ready()
内(相似 $(document).ready
),当页面 inject.js
注入成功则抛出 ready
事件,而后积累的事件栈依次出栈执行。
本文介绍了hybrid开发中为webview实现一个jssdk,介绍了 inject.js
的注入时机, inject.js
除了端能力的调用,还能够和客户端实现受权(如:微信的接入受权须要申请appid和token),同时还能够针对全部的调起指令和回调进行安全校验,屏蔽非法的调用和回调,本文只实现了最简单的调用,这些高级的设计后面文章有机会再介绍,今天敲完收工搬家过节 :P
@三水清
未经容许,请勿转载。
掘金更新比公众号晚一周左右。