js SDK 設計指南
http://blog.csdn.net/hel613/article/details/51680611 git flow 分支模型 版本發(fā)布管理 文檔生成
jsSDK 工程構建
單元測試
1:設計規(guī)范
2:了解實現細節(jié)
3:編寫mock環(huán)境
4:編碼
整個jssdk的設計有一下幾個核心問題:
- 代碼如何被使用頁面接入
- 如何實現跨域通信
- 如何實現優(yōu)雅api的設計
- 公共資源的使用
- 代碼組件化
代碼如何被使用頁面接入
這個問題涉及到幾個小問題需要討論:
- 命名空間. 動態(tài)的命名空間,在url中帶上namespace=xxx
- 樣式沖突
- 版本維護
- appid等參數的傳入
命名空間
動態(tài)的命名空間,在url中帶上namespace=xxx
樣式沖突
利用sass、less這些預編譯語言很容易
例如下面的代碼:
$name: avUI;
.#{$name}__dialog{
@include reset();
.#{$name}__dialog__header{
color: white;
}
} 怎樣制作高質量的更新日志?
指導原則
- 記住日志是寫給人的,而非機器。
- 每個版本都應該有獨立的入口。
- 同類改動應該分組放置。
- 版本與章節(jié)應該相互對應。
- 新版本在前,舊版本在后。
- 應包括每個版本的發(fā)布日期。
- 注明是否遵守語義化版本格式.
變動類型
- Added 新添加的功能。
- Changed 對現有功能的變更。
- Deprecated 已經不建議使用,準備很快移除的功能。
- Removed 已經移除的功能。
- Fixed 對bug的修復
- Security 對安全的改進
如何減少維護更新日志的精力?
在文檔最上方提供** Unreleased **區(qū)塊以記錄即將發(fā)布的更新內容。
這樣有兩大意義:
- 大家可以知道在未來版本中可能會有哪些變更
- 在發(fā)布新版本時,可以直接將Unreleased區(qū)塊中的內容移動至新發(fā) 布版本的描述區(qū)塊就可以了
有很糟糕的更新日志嗎?
當然有,下面就是一些糟糕的方式。
使用git日志
使用git日志作為更新日志是個非常糟糕的方式:git日志充滿各種無意義的信息, 如合并提交、語焉不詳的提交標題、文檔更新等。
提交的目的是記錄源碼的演化。 一些項目會清理提交記錄,一些則不會。
更新日志的目的則是記錄重要的變更以供最終受眾閱讀,而記錄范圍通常涵蓋多次提交。
無視即將棄用功能
當從一個版本升級至另一個時,人們應清楚(盡管痛苦)的知道哪些部分將出現問題。 應該允許先升級至一個列出哪些功能將會被棄用的版本,待去掉那些不再支持的部分后, 再升級至把那些棄用功能真正移除的版本。
即使其他什么都不做,也要在更新日志中列出derecations,removals以及其他重大變動。
易混淆的日期格式
在美國,人們將月份寫在日期的起始(06-02-2012對應2012年6月2日), 與此同時世界上其他地方的很多人將至寫作2 June 2012,并擁有不同發(fā)音。 2012-06-02從大到小的排列符合邏輯,并不與其他日期格式相混淆,而且還 符合ISO標準。因此,推薦在更新日志中采用使用此種日期格式。
常見問題
是否有一個標準化的更新日志格式?
并沒有。雖然GNU提供了更新日志樣式指引,以及那個僅有兩段長的GNU NEWS文件“指南”, 但兩者均遠遠不夠。
此項目意在提供一個 更好的更新日志慣例 所有點子都來自于在開源社區(qū)中對優(yōu)秀實例的觀察與記錄。
對于所有建設性批評、討論及建議,我們都非常 歡迎。
更新日志文件應被如何命名?
可以叫做CHANGELOG.md。 一些項目也使用 HISTORY、NEWS或RELEASES。
當然,你可以認為更新日志的名字并不是什么要緊事,但是為什么要為難那些僅僅是 想看到都有哪些重大變更的最終用戶呢?
對于GitHub發(fā)布呢?
這是個非常好的倡議。Releases可通過手動添加發(fā)布日志或將帶 有注釋的git標簽信息抓取后轉換的方式,將簡單的git標簽(如一個叫v1.0.0的標簽) 轉換為信息豐富的發(fā)布日志。
GitHub發(fā)布會創(chuàng)建一個非便攜、僅可在GitHub環(huán)境下顯示的更新日志。盡管會花費更 多時間,但將之處理成更新日志格式是完全可能的。
現行版本的GitHub發(fā)布不像哪些典型的大寫文件(README, CONTRIBUTING, etc.),仍可以認為是不利于最終用戶探索的。 另一個小問題則是界面并不提供不同版本間commit日志的鏈接。
更新日志可以被自動識別嗎?
非常困難,因為有各種不同的文件格式和命名。
Vandamme 是一個Ruby程序,由 Gemnasium 團隊制作,可以解析多種 (但絕對不是全部)開源庫的更新日志。
那些后來撤下的版本怎么辦?
因為各種安全/重大bug原因被撤下的版本被標記'YANKED'。 這些版本一般不出現在更新日志里,但建議他們出現。 顯示方式應該是:
0.0.5 - 2014-12-13 [YANKED]
[YANKED] 的標簽應該非常醒目。 人們應該非常容易就可以注意到他。 并且被方括號所包圍也使其更易被程序識別。
是否可以重寫更新日志?
版本維護使用“主版本.小版本.補丁號”這種有語義的命名方式管理版本。
v1.0.0
v1.5.0
v2.0.0 這樣的版本號讓使用者容易在changelog文檔中跟綜和查找。
添加新特性。對現有功能的變化(改變)。(棄用)once-stable特性在以后的版本中刪除。(刪除)棄用功能在本版本中刪除。(固定的)任何bug修復。[安全]邀請用戶升級的漏洞。
版本維護的目的是保證代碼最新,功能最全,而不用每次做了升級,通知所有使用的第三方開發(fā)者把自己頁面的代碼挨個更換。兩種比較好的方式:
- 小拖大,動拖靜:即第三方引入的js是一個動態(tài)的,或者沒有緩存沒有cdn的,然后由它帶出后面的cdn
- 隔段時間動態(tài)創(chuàng)建script
推薦使用「小拖大,動拖靜」,后面介紹組件化也要使用這個方式來按需加載代碼
小拖大,動拖靜
(function(){
.....
var url = '最新版本cdn的地址';
load(url);
}())
隔段時間動態(tài)創(chuàng)建****script
(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標簽方式:
//get url args function
function 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》
如何實現優(yōu)雅api的設計
這里的api指的是開放平臺提供的http接口,一般都會有一些標準的規(guī)范,比如:
我們設計這個函數接口的時候,應該充分考慮到將來server接口的增加,所以應該做成通用的服務,比如我們設計個sdkjs.api方法,接受四個參數:url\data\callback\method,默認如果data是函數就后面參數自動前提。
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);
}
//跨域發(fā)起請求
xDomain.send("api", {
url: _args[0],
data: _args[1],
method: _args[3] || "get",
_cbid: _cbid
});
return back;
}