網頁制作poluoluo文章簡介:今天,我們來看看yui3在框架設計中的這些犧牲和讓步,以便讓我們更加深刻的理解yui3的全貌,並將其優勢發揮至最佳。
相信每個前端工程師都有自己喜愛的javascript框架,說情感也好,道信仰也罷,javascript框架帶給人的不僅僅是便捷的開發,更有一種純粹的邏輯美感,不管是jquery曼妙的簡潔,還是yui魔術般的沙箱,都使我們產生無窮的想象。然而,js框架卻又必然無法做到面面俱到的完美無瑕,比如jquery在OO方面做出的讓步,以及yui在性能上做的犧牲,無不給人傳達一種缺憾美、一種理想的現實主義。今天,我們來看看yui3在框架設計中的這些犧牲和讓步,以便讓我們更加深刻的理解yui3的全貌,並將其優勢發揮至最佳。
1、種子的一步到位 or 顆粒化
所謂種子一步到位是指只要在頁面引入一個種子文件的script標簽,比如prototype和jquery,只要引入一個prototype.js或jquery.js就可以了,他們將各自對dom操作和event的封裝等等都囊括進一個文件中,並盡力將其做到最小,這樣做的好處是顯而易見的,使用框架非常簡單。然而yui將這些功能做了級別劃分和顆粒化設計,從概念上抽象出來“核心”、“工具”和“組件”,每個小功能放在一個文件當中,需要的時候則要自行去引用,yui文檔中給出的大量demo都采用這種方法,這種設計顯然不像jquery那樣對初學者友好,而且使用起來不夠傻瓜,為了實現一個小功能,甚至要引入三四個js文件。yui這樣做的原因有兩個,一是yui實在太大,把所有功能都搞進一個文件中確實有點不靠譜,二是為其動態加載的框架設計做鋪墊。
2、手動引入 or 動態加載
往頁面中寫js的傳統方法是,直接將js文件作為script標簽路徑寫在頁面中,使用yui也可以這樣引入頁面,但yui更推薦使用loader進行動態加載。動態加載腳本的淵源很復雜,目前來看主要原因有三,其一,頁面中手寫js標簽無論如何都會占用一個http請求,即使這個請求是一個304,動態加載的文件緩存後則不必發起真實的http請求,其二,動態加載可以實現按需加載,而且可以根據js文件之間的依賴進行去重和排序,手寫標簽加載js文件則必須讓開發者去額外關注一下文件的排序、重復等等,其三,動態加載有利於頁面代碼的語義化,這使得開發者只關心“需要什麼”,而不用去在意“如何得到”。當項目變得越發臃腫,維護成本越來越高的時候,這中小技巧會有不小的好處的。
3、邏輯啟動的單一入口 or 沙箱
我們在頁面中啟動一個js邏輯通常是放在一個類似onDomReady的方法中,如果頁面中存在多個邏輯的時候怎麼辦呢?比如,a實現了邏輯A,頁面代碼是這樣的
<script src="logicA.js" />
<script>
$.onDomReady(function(){
___LogicA.start();
});
</script>
這段代碼通常寫在頁面的尾部,這段邏輯所伴隨的html代碼是埋藏在頁面的某處,這時b要在頁面中增加邏輯B,b的做法是首先找到尾部的這段代碼,然後更改成如下模樣:
<script src="logicA.js" />
<script src="logicB.js" />
<script>
$.onDomReady(function(){
___LogicA.start();
___LogicB.start();
});
</script>
同樣,B邏輯所伴隨的html代碼也是埋藏在頁面的某處,這樣看來,js邏輯和其伴隨的html代碼如此分離,以至於到了刪減功能的時候,往往刪掉html卻忘了刪js,或者刪掉js忘了刪除html,在重用代碼的時候也會是個麻煩。同樣,在調試的時候,工程師也要打開兩個窗口分別關注js和html,即使這兩段代碼在同一個文件當中。如此則不如把代碼寫成這樣:
<!–A邏輯的html代碼段–>
<script src="logicA.js" />
<script>
$.onDomReady(function(){
___LogicA.start();
});
</script>
…
<!–B邏輯的html代碼段–>
<script src="logicB.js" />
<script>
$.onDomReady(function(){
___LogicB.start();
});
</script>
這種coding寫法正是yui所提倡的,也就是所謂的沙箱,每個邏輯包含在一個沙箱中,各司其則互不干擾。當第三者浏覽代碼的時候也立即明白這就是一個獨立的功能塊,包含典型的html結構和啟動邏輯的js,要重用,整塊拷走即可。
網頁制作poluoluo文章簡介:今天,我們來看看yui3在框架設計中的這些犧牲和讓步,以便讓我們更加深刻的理解yui3的全貌,並將其優勢發揮至最佳。
4、代碼污染 or 沙箱
剛才提到的沙箱可以解決一部分代碼污染的問題,新人閱讀代碼不用再看著亂碼般的源碼,“瞻前顧後”小心翼翼的尋找html和js的對應關系。但這種寫法也存在隱患,現在的前端開發越來越復雜也更多的使用分層,比如底層使用yui,中間層是自定義的工具庫,或者再加一個項目的widget組件庫,寫頁面邏輯則是基於這些庫進行開發。這樣的話,每段邏輯可能寫成這個樣子:
<!–A邏輯的html代碼段–>
<script src="widget.js" /><!–項目的widget庫–>
<script src="logicA.js" />
<script>
$.onDomReady(function(){
___LogicA.start();
});
</script>
盡管我們可以約定,將項目用到的所有的組件都統一加載進頁面中,但當組件越來越多的時候,就出現了上文所說的一步到位和顆粒化之間的矛盾,當每個控件單獨占用一個js文件和與之相伴隨的css皮膚,一個相對復雜的邏輯就變成了上文所說的手動引入js文件,並隨之引入一些顯而易見的弊端:
<!–復雜的A邏輯的html代碼段,使用了日歷,彈層,幻燈–>
<script src="calendar.js"/><!–日歷–>
<script src="box.js" /><!–彈層–>
<script src="tabview.js" /><!–幻燈原型–>
<script src="slider.js" /><!–幻燈–>
<script src="logicA.js" />
<script>
$.onDomReady(function(){
___LogicA.start();
});
</script>
首先,手寫大量的js文件會各自占用單獨的http請求,在者,這個場景中,slider.js繼承自tabview.js,因此要著重關注他們的順序,第三,如果別人在頁面的其他地方也使用了日歷或者幻燈,還要再加兩個相同的js標簽?其實,說到這裡,我們已經可以隱約看到大項目團隊開發的影子了,在一個迭代及其頻繁的項目中,開發者需要在短的時間內完成一個復雜頁面的某個功能的開發,而且不能影響到頁面的其他功能,畢竟,每添加一個功能,QA mm們都要將與之牽連的所有功能都要回歸,可辛苦了我們的QA mm們。在這種情況下,一個功能的開發可能和同一個頁面其他功能的開發相互並行。互相不屬於同一個項目組,也不知曉對方的實現。這種模式則是我們經常遇到的多人開發,沖突也大都由此產生,每個功能單獨看來是正確的,合並到一起會產生沖突和bug,這時調試bug則需要兩個工程師同時進行,很麻煩。甚者,當組件升級了,比如,tabview.js再繼承自tab.js,則又要去聯系各個工程師,將每個引用tabview.js的地方之前再加上一個tab.js,很麻煩。我們說,這種多人協作模式所帶來的沖突也是代碼污染的一種,因為每個人只能掌控類似tms區塊那麼巴掌大的地方,所組成的最終頁面是什麼樣,都不知道。更何況,這種生猛的引用js,也會阻塞頁面加載,占用http請求,影響性能。
鑒於此,yui將js的動態引入機制也並入其沙箱設計之中,我要引用的只是一個名字,比如slider.js,他依賴tabview.js,tabview.js依賴tab.js,這樣我只需引用一個名叫”slider”的東西即可,不用操心他依賴什麼,更不用管如何引入到頁面中,yui都幫我們搞定:
<script>
TB.addmoudle({
___logicA:{
______fullpath:'logica.js',
______requires:['slider']
___}
}).use('logicA',function(Y){
___LogicA.start();
});
</script>
當我們看最終組成的頁面的時候,看到的只是埋藏在頁面各個角落的這些簡短的結構一致的js代碼段。很易理解,這點代碼也不用像長的代碼塊壓成一行。(更多細節可參照 前端弱架構導致的代碼污染 )
網頁制作poluoluo文章簡介:今天,我們來看看yui3在框架設計中的這些犧牲和讓步,以便讓我們更加深刻的理解yui3的全貌,並將其優勢發揮至最佳。
5、顆粒化 or http請求數
這的確是一對矛盾,顆粒化帶來了項目開發、管理、和代碼重用的高效率,卻又引入了更多的http請求數,好在yui提供了combo,可以將所有的http請求合並成一個。只需在YUI引入的時候配置下combo屬性即可,高顆粒化的請求數瞬間降低一倍以上。在之前做雅虎關系的時候,在yui2和yui3pre並存的情況下,可以將請求降低到4個,yui2和3各一個種子,各自一個combo。當然這是在hack掉yui的loader的前提下。yui默認不會合並非yui文件(更多細節可以閱讀基於yui的團隊開發)。即使這樣,我們仍然可以控制我們的http請求數,在不hack yui的情況下,可以解決部分性能問題。
6、懶惰加載 or 即時加載即時執行
上文提到,邏輯依賴沙箱,沙箱依賴的js文件則是延時加載的,這樣就導致一個問題,當頁面比較龐大的時候,會等待頁面js加載完畢才會渲染動作,這樣的用戶體驗不佳,而即時加載即使運行則可以渲染出模塊後隨即渲染動作,當網速一定的時候,兩者看似是一對不可調和的矛盾,yui 動態加載的機制比較折中的處理了這個問題,A邏輯需要a.js,B邏輯需要b.js,A邏輯則只會在a.js加載完成後執行,而不管b.js是否加載完成,而當A需要a.js和b.js的時候,A則需要等待a.js和b.js都加載完成才會執行,B邏輯只需判斷當前是否已經加載b.js,如果b.js已經在其他模塊中引入進來,B則可以立即執行。但確定的是,所有的js的引入一定是在domReady後執行的,也就是說,不管怎樣,動作的渲染一定是在頁面html結構出來後才開始執行的,用戶體驗上還是會有損失。
7、面向接口的設計 or 面向dom的設計
我們知道jquery的插件習慣將所有的動作都加載到一個$(‘#id’)上,使用的時候只要執行類似$(‘#id’).init()即可。這看起來簡潔明快,開發者的邏輯的思路也始終沒有離開“節點”,很方便理解,而對yui3 的node擴展就不那麼方便,從yui3的pre版到正式版,對node擴展的方法在不斷的修改(更多細節看這裡:擴展yui3 node的定時器),這也可以看出yui設計者在對node擴展性設計時的糾結和苦惱:要不要允許開發者去擴展node節點呢?大概是因為設計者們對prototype先天的弊端心有余悸。目前看來,設計者還沒有完全走出糾結,盡管對node擴展相比yui3第一個預覽版方便了很多,開發者仍然不能像寫jquery插件那樣優雅自如的對node進行擴展。相反,zakas卻將更多的精力放在了widget接口的設計上,在這一點上,相比jquery,yui3則具有無可限量的優越性,因為在yui3中,組件不僅僅是組件,而是一個有血有肉的生命體,他可以出生,可以成長,可以被改造,可以死亡,組件在這些復雜的運行時環境中自我錘煉,因此,一個復雜頁面在yui3的技術體系中,變成了一個由無數組件鏈接而成的生態系統,這種生物鏈所帶來的設計創新和新視野是其他js框架無論如何也無法超越的。關於yui3的組件開發更多細節可以參照:基於yui3的組件開發1 和 克軍在D2上的分享《從yui2到yui3看前端的演變》
8、苗條的身材 or 龐大的身軀
說到這裡,大概會有很多人拍磚。其實jquery和yui同屬兩類不同的js框架,一個苗條纖細,一個身重如山,兩者之間其實沒有什麼水深火熱,只是使用范圍不同罷了,類似jquery的輕框架的使用范圍是博客級別的小網站,尤其適合單人開發,代碼寫一次不再修改,而且很適合初學者學習使用,給初學者帶來自信。yui則使用與企業級的項目中的團隊開發,項目維護周期遠遠超過開發周期,因此yui性能一定比不過jquery,jquery的續航能力也一定不如yui,沒有最優秀,只有最適合。當然這自然也擋不住我個人對迷人的jquery的喜愛,只要我們能從各個js框架學到東西,能提高自己做前端架構的能力,就ok。
說了這麼多,其實只有一個觀點,人無完人,框架無完框架,缺憾之處必有權衡。以上YY,歡迎拍磚!