今天看到一篇《javascript sdk設計指南》,內容篇幅比較多,很多實際是問題的列舉,但是資訊量太大,所以我結合之前做微博開放平臺和運營活動平臺的經驗,說下jssdk的設計和一些核心問題的解決方案。
一個jssdk一般是指提供給第三方人員使用的一段js,通過這個js實現一些平臺化產品提供的服務,比如微博的jssdk。整個jssdk的設計有一下幾個核心問題:
- 程式碼如何被使用頁面接入
- 如何實現跨域通訊
- 如何實現優雅api的設計
- 公共資源的使用
- 程式碼元件化
先說第一個問題
程式碼如何被使用頁面接入
這個問題涉及到幾個小問題需要討論:
- 名稱空間
- 樣式衝突
- 版本維護
- appid等引數的傳入
名稱空間
在「名稱空間」部分,需要做到不汙染環境,保護好自己
,即不要對本來的頁面造成命名的破壞,只是用一個名稱空間,又要考慮到第三方頁面的複雜性,防止跟錯綜複雜的名稱空間衝突。
要做到這點,需要我們在名稱空間命名的時候多注意下,儘量不要使用業內通用的命名方法,比如駝峰,名字儘量起的怪一些,偏一些,一般,要麼使用_
開頭(甚至多個),要麼使用專案代號這些不太被別人想到的名字,嗯,我記得有人名稱空間用av
,很好呀!
還有一種方式是動態的名稱空間,在url中帶上namespace=xxx
,本節結束後面會統一給出示例
樣式衝突
除了js名稱空間問題,如果jssdk帶有UI元件,那麼還需要考慮css的樣式衝突問題,這裡不用多說,記住以下幾點:
- 一些複雜的widget可以使用iframe方式引入
- 不使用id
- 使用帶字首的class命名,前面用一個class最好包裹
- 自己做reset!
- 跟js相關的class要有特殊的約定(比如
_J-xxx
)或者使用data-id
代替
其實利用sass、less這些預編譯語言很容易
例如下面的程式碼:
1 2 3 4 5 6 7 8 |
$name: avUI; .#{$name}__dialog{ @include reset(); .#{$name}__dialog__header{ color: white; } } |
版本維護
版本維護的目的是保證程式碼最新,功能最全,而不用每次做了升級,通知所有使用的第三方開發者把自己頁面的程式碼挨個更換。所以這裡版本維護不應該暴漏給使用者,比如在url使用版本號,到了2.0版本,通知使用方替換,這是不合理的,總有些公司或者人不配合的。最好的方法是設計的時候就要考慮到這個問題。
一般有兩種比較好的方式:
- 小拖大,動拖靜:即第三方引入的js是一個動態的,或者沒有快取沒有cdn的,然後由它帶出後面的cdn
- 隔段時間動態建立script
推薦使用「小拖大,動拖靜」,後面介紹元件化也要使用這個方式來按需載入程式碼
小拖大,動拖靜
核心程式碼示例
1 2 3 4 5 |
(function(){ ..... var url = '最新版本cdn的地址'; load(url); }()) |
隔段時間動態建立script
程式碼示例:
1 2 3 4 5 6 7 8 9 |
(function () { var s = document.createElement('script'); s.type = 'text/javascript'; var t = +new Date; t -= %864E5; s.src = 'http://xxx.com/sdk.js?t='+t; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })(); |
appid等引數的傳入
一般在引入sdkjs程式碼的時候需要加引數或者版本號,比如開放平臺需要配置appid
,所以url寫法是:
sdk.js?appid=xxxx&namespace=xxx
。jssdk需要拿到url中的這些引數,方法有以下兩種比較通用的:
- 給script標籤增加特殊屬性,例如
<script src="path/sdk.js?appid=123" id="_jssdk">
- 使用查詢script標籤方式:
12345678910111213//get url args functionfunction parserUrl(){var scripts = document.getElementsByTagName("script"),len = scripts.length,url;if (len > 0) {for (var i = 0; i < len; i++) {if (scripts[i].src.indexOf("path/to/sdk.js") !== -1) {return scripts[i].src.split("?").pop();}}}}
所以appid,namespace這些都可以解析出來
如何實現跨域通訊
對於不在一個域名下的第三方頁面引入的jssdk少不了的是跨域請求,這塊移動上可以直接使用postMessage
方法,將來可以使用xhr2+CORS,相相容IE,可以參考《三水清跨域tag》的內容,這裡不做過多介紹
如何實現優雅api的設計
這裡的api指的是開放平臺提供的http介面,一般都會有一些標準的規範,比如:
我們設計這個函式介面的時候,應該充分考慮到將來server介面的增加,所以應該做成通用的服務,比如我們設計個sdkjs.api
方法,接受四個引數:url\data\callback\method,預設如果data是函式就後面引數自動前提。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
api: function(url, data, callback, method) { var _args = $.toArray(arguments), _callback = _args[2] || $.emptyFn; if (_args.length < 3) { throw Error("api arguments length wrong"); } if (!$.isString(_args[0]) || !$.isObject(_args[1]) || !$.isFunction(_callback)) { throw Error("api arguments format error"); } var _cbid = 0; if ($.isFunction(_callback)) { _cbid = _CallbackManager.add(_callback); } //跨域發起請求 xDomain.send("api", { url: _args[0], data: _args[1], method: _args[3] || "get", _cbid: _cbid }); return back; } |
公共資源的使用
公共資源的使用,指的是一些跟宿主環境共享的資源,比如cookie、localstorage這些,使用的時候應該做字首處理,儘量不汙染宿主頁面環境,同時保證不被輕易的刪除。
程式碼元件化
程式碼的組織在一些帶有UI的jssdk中使用較多,比如按需載入某個UI模組。這時候就充分利用到了第一節提到的「小拖大,動拖靜」的引入方式,一開始小檔案我們叫seed,裡面有UI元件和sdk主程式碼的url,seed.js載入後,先載入sdk的核心js檔案,然後如果使用某個UI元件,就按需載入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var MAP = { core: ['sdk-core.js'], ui: { loginDialog: ['path/loginDialog.css', 'path/loginDialog.js'] } } load(MAP.core); //使用 SDKJS.ready(function($){ //$實際是SDKJS $.use('loginDialog', function(loginDialog){ loginDialog(xxxxx); }) }); |
其中.use
方法,有些類似require
方法,起到按需載入的功能。