上一篇博文受到了大家的很多關注,謝謝大家的捧場。所謂乾貨,也是我的願景,就是希望能讓大家實實在在看得見實現原理,在自己的項目裡面用的起來的,能騙得老闆的獎金,能完成KPI指標的東西^_^。Kit作為一個UI庫,我並沒有打算讓大家都來學習我的Kit的Core,背熟我的API,這種跟風的學習方式一點意義都沒有,今天jQuery熱,大家都是學jQ,明天SeaJs火了,大家都去炒SeaJs,所以我在KitJs裡面,專門為jQ的使用者準備了一個文法糖(Suger.js),完全類比jQ的API,除了實現,介面都一樣,也方便大家直接拿來主義的改造Kit的組件。當然,作為一個純技術Fan來說,深入理解一門技術是如何?的,遠比拿來主義更有趣的多^_^。當然了,如果你出於KPI考慮,或者老闆的老闆的項目獎金,直接拿來主義抄襲Kit的組件代碼,完成你的KPI,我也不介意這樣的行為,只要您喝水不忘挖井人,在和同事吹水的時候,也能宣傳一個KitJs,我就很感激您了。同時,Kit也是一個很年輕的庫,出於不斷的發展之中,有一些BUG以及瀏覽器安全色問題,在所難免,我一個人也精力有限,在這個前端戰火紛飛的年代,歡迎更多志同道合的基友一起把他搞大,共同進步。
同時,今天發布了一個kitjs的對話方塊組件,demo地址為http://xueduany.github.com/KitJs/KitJs/demo/Dialog/demo.html
(一)Kit目錄格式
言歸正傳,在KitJs裡,kit.js是作為核心的Core檔案的存在,他包含了一些最常用的Dom以及Object,繼承的操作,同級目錄下按照功能的劃分擴充了一批string.js,math.js等等都是為了實現特定方向功能的擴充。每一個獨立的js檔案都包含一個Class的構造器,以及一個全域對象的執行個體,
以kit.js為例,包含了$Kit類,以及$Kit類的執行個體$kit(以$開頭是為了避免與常用的變數衝突),
其他各類,都以Link的方式,掛在$Kit,以及$kit執行個體執行個體上,如math.js,包含了$Kit.Math類,以及$kit.math執行個體,這樣保證全域範圍裡只有$Kit和$kit兩個汙染。同時,在kit.js,我們定義了一個命名空間叫做$kit.ui,在物理目錄下,以kit.js同級的Widget目錄,一字排開,多個首字母大寫的目錄
widget目錄下所有目錄都是kitjs的組件目錄,每個獨立js檔案只包含一個獨立組件的class構造器(非執行個體),同時可以相容commonJs的module模式(可以符合CommonJs的Modules/1.1 規範,以及AMD方式改造,具體改造方式後面會以後會詳細提及)
(二)Kit組件預設代碼模板,注釋符合jsdoc規範
我們以對話方塊組件舉例,每個組件都類似如下
首先是jsdoc的注釋,@class申明是一個什麼類,@require xxx.js,申明依賴哪些組件
(三)構造器以及初始化方法
每個類都是標準的function(config){}的方式定義個構造器,這裡需要注意的是,每個kitjs組件的構造器預設預留一個config參數,作為個人化配置的輸入,
同時在類的構造器,有個一個靜態成員,defaultConfig對象,用來存放kitjs組件的預設配置
在使用kitjs的組件,首先是需要通過new Instance的方式new $kit.ui.Dialog.YesOrNo,初始化一個新的執行個體對象出來,這是僅僅是初始化了一個js的組件對象,還沒有HTML,需要執行init方法,建立HTML,加入doc中,等於給靈魂澆上血肉^_^。
可能有同學會問,為什麼不把init方法直接放在構造器裡面,而要另外單獨放出來?
1是因為在繼承時候需要執行個體化父類,當子類繼承於父類的時候,會設定子類的prototype對象為父類的new Instance新的執行個體對象,如果在構造器裡面放了init的初始化方法,會導致父類的HTML被直接執行,產生垃圾代碼,
2是因為考慮懶載入的情況,需要HTML代碼在恰當的時間執行,而不是一開始初始化時立即執行
所以使用kitjs組件的預設是
執行個體化之後,執行init方法(init方法會返回當前組件對象,有return代碼7)
可以發現,在dialog中所有API method都是掛在prototype上,通過原型擴充的方式實現繼承以及傳遞給執行個體對象
觀察$kit.ui.Dialog.YesOrNo組件的構造器代碼,
(四)KitJs的繼承
他通過$kit.inherit方法申明了與$kit.ui.Dialog對象的繼承關係,這裡會有同學要問,為什麼要在構造器裡面繼承,而不是直接寫在外面?
原因是:
1.kitjs是一個基於prototype維護繼承關係的
2.要使用kitjs的組件,必須要執行個體化該組件對象,每個組件都是通過new Instance的方式,通過構造器建立的
所以我把繼承關係的執行放在代碼的構造器中,這樣在執行個體化一個新的組件時,就會順著當前組件的構造器的繼承方法,逐級去繼承到他父類的成員以及方法。
當子類需要修改父類的方法時,只需要在子類的prototype裡從定義一個同名的method即可覆蓋父類的繼承方法。
在命名上,kitjs遵循,子類延續父類的類名作為Namespace,一直鏈下去,如的$kit.ui.Dialog,$kit.ui.Dialog.YesOrNo
kitjs的繼承實現也很簡單
執行個體化一個父類對象,將父類的執行個體所有成員copy到子類的原型上,然後重設子類的原型的構造器為子類構造器,再給子類構造器掛一個link,指向父類,通過$kit.inherit方法,在子類$kit.ui.Dialog.YesOrNo執行個體化的過程中,就可以繼承父類$kit.ui.Dialog的所有子類不存在的成員,實作類別似靜態語言的繼承
(五)config參數,HTML與Css的耦合拆解/換膚?
kit的組件構造器習慣傳入一個 Map類型的參數,從來個人化組件,在kit組件初始化的時候,會自動用使用者提交的config參數覆蓋預設的defaultConfig後開始初始化。
對於任何一個組件來說,擺脫不了是HTML結構的變化,以及Css樣式的改變
kit把這種耦合分解在config的參數配置裡面,
首先是使用HTML模板技術,kit提倡使用$kit.newHTML方法直接根絕HTML String,產生HTML DOM插入文檔流,
所以我們抽取組件的大概HTML內容,封裝成HTML String模板,存放在組件的defaultConfig裡面,如果使用者需要修改HTML模板,自己在初始化的時候使用自訂的config,覆蓋預設的defaultConfig裡面的模板欄位即可,
在HTML模板與Css的耦合分解上,kit用了一個技巧就是把className用js模板的方式,分解開來
通過在init方法中的$kit.tpl 將config 中的html以${xxx}的方式對應config中的xxx做替換
同時所有的樣式都在css裡面設定,
如果有多套皮膚需要切換,可以選擇在初始化時候,通過config指定${cls}對應的實際className來達到修改模板的className,來達到換膚的效果。
(六)小結
基本上,透過對$kit.ui.Dialog.YesOrNo組件的程式碼分析,我們對kitjs的組件實現結構有了一個大概的瞭解。其實設計一個頁面組件並不難,但是設計一個能適應各種要求,在各種場合下,可以很快速的變形,並適應開發,是一個很難的要求。kit通過對HTML模板以及Css的拆分,自訂config參數與defaultConfig的配合,子類通過繼承的方式獲得父類的屬性以及方法,同時根據不同的業務需要重構相關代碼,基本上可以靈活的滿足各種層次,各種環境下的業務UI組件需求。
下一篇,我將會帶領大家去詳細瞭解下kit比較核心的事件處理機制,以及kit作為一個javascript庫,是如何?滑鼠手勢這種進階的Event控制介面的,敬請期待!