QWrap入門之apps種子應用
就像是一棵樹有很多果實一樣,QWrap也有很多apps,本文講解種子應用。“種子”是沿用YUI3的說法,種子應用是解決模塊加載問題的應用,包括:模塊預加載、異步按需加載、模塊應用。
apps果實篇之:種子
或許有些同學對異步加載模塊不大熟悉,沒關系,我們先感性的看一下這段代碼
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
- <html>
- <head>
- <title>QW應用之一:seed_youa</title>
- <meta http-equiv="Content-Type" content="text/html; charset=GB2312" />
- <script type="text/javascript" src="http://dev.qwrap.com/resource/js/apps/seed_youa.combo.js"></script>
- </head>
- <body>
- <div id="div1">div1-----<span class="content">時間</span></div>
- <div id="div2">div2-----<span class="content">時間</span></div>
- <input type=button value="點擊后按需加載后并修改div1/div2" onclick="test()"/>
- <script type="text/javascript">
- function test(){
- QW.use('jQuery,YouaCore',function(){
- $('#div1').css('color','red').find('>.content').html(new Date()+'');//這段代碼用到了jquery用法
- W('#div2').css('color','blue').query('.content').html(new Date().format('YYYY-MM-dd hh:mm:ss'));//這段代碼用到了Youa版的QWrap
- });
- }
- </script>
- </body>
1. 如果用戶瀏覽此網頁時,只加載了一個體積很小的seed_youa.combo.js這個js(其實就是seed_youa.js(http://dev.qwrap.com/resource/js/apps/seed_youa.js)),這個js經yui壓縮后的大小為4K。
2. 當用戶點擊按鈕時,按鈕對應的js用到jQuery與YouaCore,點擊后才會去加載那兩個jQuery與YouaCore對應的js,之后才有運行效果。
3. 第二次點擊按鈕,由于前面已經加載過js,所以這次會直接運行按鈕事件。
分析一下以上三步,分兩種情況。
A. 對于只進行了第一步操作的用戶,他們用seed_youa.js替代了jQuery與YouaCore兩個js的流量(這是好處甲);
B. 對于進行過第二步操作的用戶,由于以上流量的節約,會讓用戶提前能夠點擊按鈕(這是好處乙),點擊后異步加載需要的js,加載完后事件才運行,即點擊與運行存在一個時間差(這是壞處甲),并且三個js都用到了,相對于傳統寫法,http多了一個seed_youa.combo.js(這是壞處乙)。
另外,對于頁面程序員來說,他在寫按鈕事件時,只需要知道自己的這段js用到了哪些模塊,而不用關心這些模塊是否已經加載(這是好處丙),不過,他引用模塊的方式有了一些變化(少鍵入html代碼,多鍵入js代碼)。
好處甲、好處乙、好處丙、壞處甲、壞處乙,還有些沒有分析到的好處與壞處,反正有利有弊。
那么,在什么情況下利大于弊、什么情況下弊大于利,而我們該如何興利除弊?
如果這個頁面的用戶100%不會點擊按鈕,則幾乎沒壞處,只有好處甲、好處丙。
如果頁面100%的用戶會點擊按鈕,則需要權衡再權衡利弊再想辦法。
好的,我們先暫且擱置這些細節,因為以上內容已足夠讓同學們對“異步按需加載”有了感性的認識。我們把提供這種異步按需加載的js叫種子(seed)。
我們看一下seed_youa.js,它是一個組合js(即前面一篇文章所說的B類apps:開發時可以是多個文件,但上線時會先合并后上線。)
它由三個js組成:
- document.write('<script type="text/javascript" src="' + srcPath + 'core/core_base.js"><\/script>');
- document.write('<script type="text/javascript" src="' + srcPath + 'core/module.h.js"><\/script>');
- document.write('<script type="text/javascript" src="' + srcPath + 'apps/youa_modules_config.js"><\/script>')
core/core_base.js是QWrap的主干js
core/module.h.js是模塊管理器,也是一個Helper,提供三個方法:addConfig、use、provide,代碼參見:http://dev.qwrap.com/resource/js/core/module.h.js
youa_modules_config.js是youa項目只所使用到的常用模塊配置,通過addConfig進行模塊配置,代碼如下:
- /*Lib Module*/
- QW.ModuleH.addConfig({
- YouaCore: {
- url: '//apps/core_dom_youa_lazy.combo.js',
- loadedChecker:function(){
- return !!(QW.W);
- }
- },
- Ajax: {
- url: '//components/ajax/ajax.youa.js',
- requires: 'YouaCore'
- },
- Anim: {
- url: '//components/animation/anim.js',
- requires: 'YouaCore'
- },
- Cookie: {
- url: '//components/cache/cache.js',
- requires: 'YouaCore'
- },
- Storage: {
- url: '//components/cache/cache.js',
- requires: 'YouaCore'
- },
- Drag: {
- url: '//components/drag/drag.js',
- requires: 'YouaCore'
- },
- Editor: {
- url: '//components/editor/editor.js',
- requires: 'YouaCore,Panel'
- },
- Panel: {
- url: '//components/panel/panel.js',
- requires: 'YouaCore'
- },
- Suggest: {
- url: '//components/suggest/suggest.js',
- requires: 'YouaCore'
- },
- "Switch": {
- url: '//components/switch/switch.js',
- requires: 'YouaCore'
- },
- Tree: {
- url: '//components/tree/tree.js',
- requires: 'YouaCore'
- },
- Valid: {
- url: '//components/valid/valid.js',
- requires: 'YouaCore'
- },
- jQuery: {
- url: 'http://common.cnblogs.com/script/jquery.js',
- loadedChecker:function(){
- return !!(window.jQuery && window.jQuery.fn);
- }
- }
- });
- /*Logic Module*/
- QW.ModuleH.addConfig({
- "User": {
- url: '//global/userv3.js',
- requires: 'YouaCore',
- loadedChecker: function() {
- return !!window.topbar;
- }
- },
- ShopMap: {
- url: '//sp/map/shopmap.js',
- requires: 'YouaCore'
- }
- });
看到這個配置文件的同學也許會問:模塊添加得越來越多會怎么辦?為什么不像YUI一樣,在各個模塊的js里進行addConfig,而要集中起來addConfig?
不錯,理論上是會越來越大,但是,實際上最大能大到多少?并且不一定是所有的模塊配置都要放在這里面。在頁面的js里也可以QW.addConfig('MyPageJs','//my.js')的。所謂的太大,只是個理論問題,不是個實際問題。
YUI在各個模塊的js里進行add,有好處,也有壞處。
好處就是多了個沙箱機制,沙箱理論看起來很嚴謹,可以滿足YUI的嚴謹性潔癖----其實我一直沒有理解它對實用者都有啥好處。
而壞處,也有很多。例如:
1. 需要模塊的代碼來適應他的加載機制。----例如,jQuery也可以算是個模塊,憑什么要讓獨立的jquery來改代碼?
2. 假設我改了jQuery的代碼來適應這loader,那是不是也損失了jQuery的原來的用法,即預加載模式用法(在HTML的Header里引用jquery.js,在頁面直接用$('#id').show())。
3. 例如use('Editor',function(){})時,其實由于依賴關系,會按需加載YouaCore,Drag,Panel,Editor四個js,因為依賴關系是在各自的js里,理論上無法將四個請求合并成一個請求,也無法在請求editor.js時就并行請求drag.js。
4. 如果客戶端緩存了js,我們更新Editor.js后,use('Editor',function(){})無法知道是否需要在editor.js后添加版本號。
5. 如果有復合模塊,例如core_dom_youa_lazy.js其實是同時擁有很多小模塊功能的一個js,那在drap.js里如何定義依賴?
而QWrap的“集中配置”方式,上面幾條都不是什么問題。
對于第一條第二條,已經解決,即:addConfig時的loadedChecker參數。
例如,這樣配置jQuery:
- QW.ModuleH.addConfig({
- jQuery: {
- url: 'http://common.cnblogs.com/script/jquery.js',
- loadedChecker:function(){
- return !!(window.jQuery && window.jQuery.fn);
- }
- }
- });
這個jQuery引用的是博客園網站的,我們沒有作任何修改。
如果頁面已經預加載了jquery,運行QW.use('jQuery',function(){})也不會再次加載jquery.js。
當然,用戶還可以保持習慣,用以前的預加載經典用法。
對于第三條合并請求與并行請求,QW目前還沒做,不過,只需調整一下module.h.js里的某一段代碼即可。在源代碼里有說明:
- function loadsJsInOrder() {
- //瀏覽器不能保證動態添加的ScriptElement會按順序執行,所以人為來保證一下
- //參見:http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/
- //測試幫助:http://1.cuzillion.com/bin/resource.cgi?type=js&sleep=3&jsdelay=0&n=1&t=1294649352
- //todo: 目前沒有充分利用部分瀏覽器的并行下載功能,可以改進。
- //todo: 如果服務器端能combo,則可修改以下內容以適應。
- var moduleI = loadingModules[0];
- function loadedDone() {
- moduleI.loadStatus = 2;
- var cbs = moduleI.__callbacks;
- for (var i = 0; i < cbs.length; i++) {
- cbs[i]();
- }
- isLoading = false;
- loadsJsInOrder();
- }
- if (!isLoading && moduleI) {
- //alert(moduleI.url);
- isLoading = true;
- loadingModules.splice(0, 1);
- var checker = moduleI.loadedChecker;
- if (checker && checker()) { //如果有loaderChecker,則用loaderChecker判斷一下是否已經加載過
- loadedDone();
- } else {
- loadJs(moduleI.url.replace(/^\/\//, QW.PATH), loadedDone);
- }
- }
- }
對于第四條緩存,由于是統一配置,所以只需在發布時,在配置文件的js后面加上對應js的md5碼的前N位即可。
例如,線上的配置可能是:
- Ajax: {
- url: '//components/ajax/ajax.youa.js?111111.js',
- requires: 'YouaCore'
- },
- Anim: {
- url: '//components/animation/anim.js?222222.js',
- requires: 'YouaCore'
- }
頁面引用的種子,也是在后面加上它的md5碼的前N位。
這樣做的好處是:每次上線后,用戶也只需下載有過修改的js。
對于第五條的復合模塊問題,由于是統一配置,所以根本就不是問題。
說了seed_youa.js的這么多好處,當然還要說明一個限制:項目中需要有一個水平還過得去的js同學,來負責這個統一的模塊配置文件。
關于module.h.js還有挺多內容要講的,不過,絕大同學不用關心它的實現;關心他的實現的同學大多也看過NK行的YUI3種子,再看這個兩百行的module.h.js,應該是小case,這里先略過。
小結一下,QWrap的這個種子應用是綠色版的,并且小巧、靈活,可以直接復制過去引用。不過需要注意一下你所放的路徑,防止QW.PATH的值計算有誤,或者強制改掉QW.PATH的值。
附:QWrap網址:http://www.qwrap.com
原文:http://www.cnblogs.com/jkisjk/archive/2011/04/15/qwrap_apps_seed_youa.html
【編輯推薦】