拖稿了很久的「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
@三水清
未經容許,請勿轉載。
掘金更新比公衆號晚一週左右。