QWrap入門之a(chǎn)pps種子應(yīng)用
就像是一棵樹有很多果實(shí)一樣,QWrap也有很多apps,本文講解種子應(yīng)用。“種子”是沿用YUI3的說(shuō)法,種子應(yīng)用是解決模塊加載問(wèn)題的應(yīng)用,包括:模塊預(yù)加載、異步按需加載、模塊應(yīng)用。
apps果實(shí)篇之:種子
或許有些同學(xué)對(duì)異步加載模塊不大熟悉,沒(méi)關(guān)系,我們先感性的看一下這段代碼
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
- <html>
- <head>
- <title>QW應(yīng)用之一: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">時(shí)間</span></div>
- <div id="div2">div2-----<span class="content">時(shí)間</span></div>
- <input type=button value="點(diǎn)擊后按需加載后并修改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. 如果用戶瀏覽此網(wǎng)頁(yè)時(shí),只加載了一個(gè)體積很小的seed_youa.combo.js這個(gè)js(其實(shí)就是seed_youa.js(http://dev.qwrap.com/resource/js/apps/seed_youa.js)),這個(gè)js經(jīng)yui壓縮后的大小為4K。
2. 當(dāng)用戶點(diǎn)擊按鈕時(shí),按鈕對(duì)應(yīng)的js用到j(luò)Query與YouaCore,點(diǎn)擊后才會(huì)去加載那兩個(gè)jQuery與YouaCore對(duì)應(yīng)的js,之后才有運(yùn)行效果。
3. 第二次點(diǎn)擊按鈕,由于前面已經(jīng)加載過(guò)js,所以這次會(huì)直接運(yùn)行按鈕事件。
分析一下以上三步,分兩種情況。
A. 對(duì)于只進(jìn)行了第一步操作的用戶,他們用seed_youa.js替代了jQuery與YouaCore兩個(gè)js的流量(這是好處甲);
B. 對(duì)于進(jìn)行過(guò)第二步操作的用戶,由于以上流量的節(jié)約,會(huì)讓用戶提前能夠點(diǎn)擊按鈕(這是好處乙),點(diǎn)擊后異步加載需要的js,加載完后事件才運(yùn)行,即點(diǎn)擊與運(yùn)行存在一個(gè)時(shí)間差(這是壞處甲),并且三個(gè)js都用到了,相對(duì)于傳統(tǒng)寫法,http多了一個(gè)seed_youa.combo.js(這是壞處乙)。
另外,對(duì)于頁(yè)面程序員來(lái)說(shuō),他在寫按鈕事件時(shí),只需要知道自己的這段js用到了哪些模塊,而不用關(guān)心這些模塊是否已經(jīng)加載(這是好處丙),不過(guò),他引用模塊的方式有了一些變化(少鍵入html代碼,多鍵入js代碼)。
好處甲、好處乙、好處丙、壞處甲、壞處乙,還有些沒(méi)有分析到的好處與壞處,反正有利有弊。
那么,在什么情況下利大于弊、什么情況下弊大于利,而我們?cè)撊绾闻d利除弊?
如果這個(gè)頁(yè)面的用戶100%不會(huì)點(diǎn)擊按鈕,則幾乎沒(méi)壞處,只有好處甲、好處丙。
如果頁(yè)面100%的用戶會(huì)點(diǎn)擊按鈕,則需要權(quán)衡再權(quán)衡利弊再想辦法。
好的,我們先暫且擱置這些細(xì)節(jié),因?yàn)橐陨蟽?nèi)容已足夠讓同學(xué)們對(duì)“異步按需加載”有了感性的認(rèn)識(shí)。我們把提供這種異步按需加載的js叫種子(seed)。
我們看一下seed_youa.js,它是一個(gè)組合js(即前面一篇文章所說(shuō)的B類apps:開發(fā)時(shí)可以是多個(gè)文件,但上線時(shí)會(huì)先合并后上線。)
它由三個(gè)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是模塊管理器,也是一個(gè)Helper,提供三個(gè)方法:addConfig、use、provide,代碼參見(jiàn):http://dev.qwrap.com/resource/js/core/module.h.js
youa_modules_config.js是youa項(xiàng)目只所使用到的常用模塊配置,通過(guò)addConfig進(jìn)行模塊配置,代碼如下:
- /*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'
- }
- });
看到這個(gè)配置文件的同學(xué)也許會(huì)問(wèn):模塊添加得越來(lái)越多會(huì)怎么辦?為什么不像YUI一樣,在各個(gè)模塊的js里進(jìn)行addConfig,而要集中起來(lái)addConfig?
不錯(cuò),理論上是會(huì)越來(lái)越大,但是,實(shí)際上最大能大到多少?并且不一定是所有的模塊配置都要放在這里面。在頁(yè)面的js里也可以QW.addConfig('MyPageJs','//my.js')的。所謂的太大,只是個(gè)理論問(wèn)題,不是個(gè)實(shí)際問(wèn)題。
YUI在各個(gè)模塊的js里進(jìn)行add,有好處,也有壞處。
好處就是多了個(gè)沙箱機(jī)制,沙箱理論看起來(lái)很嚴(yán)謹(jǐn),可以滿足YUI的嚴(yán)謹(jǐn)性潔癖----其實(shí)我一直沒(méi)有理解它對(duì)實(shí)用者都有啥好處。
而壞處,也有很多。例如:
1. 需要模塊的代碼來(lái)適應(yīng)他的加載機(jī)制。----例如,jQuery也可以算是個(gè)模塊,憑什么要讓獨(dú)立的jquery來(lái)改代碼?
2. 假設(shè)我改了jQuery的代碼來(lái)適應(yīng)這loader,那是不是也損失了jQuery的原來(lái)的用法,即預(yù)加載模式用法(在HTML的Header里引用jquery.js,在頁(yè)面直接用$('#id').show())。
3. 例如use('Editor',function(){})時(shí),其實(shí)由于依賴關(guān)系,會(huì)按需加載YouaCore,Drag,Panel,Editor四個(gè)js,因?yàn)橐蕾囮P(guān)系是在各自的js里,理論上無(wú)法將四個(gè)請(qǐng)求合并成一個(gè)請(qǐng)求,也無(wú)法在請(qǐng)求editor.js時(shí)就并行請(qǐng)求drag.js。
4. 如果客戶端緩存了js,我們更新Editor.js后,use('Editor',function(){})無(wú)法知道是否需要在editor.js后添加版本號(hào)。
5. 如果有復(fù)合模塊,例如core_dom_youa_lazy.js其實(shí)是同時(shí)擁有很多小模塊功能的一個(gè)js,那在drap.js里如何定義依賴?
而QWrap的“集中配置”方式,上面幾條都不是什么問(wèn)題。
對(duì)于第一條第二條,已經(jīng)解決,即:addConfig時(shí)的loadedChecker參數(shù)。
例如,這樣配置jQuery:
- QW.ModuleH.addConfig({
- jQuery: {
- url: 'http://common.cnblogs.com/script/jquery.js',
- loadedChecker:function(){
- return !!(window.jQuery && window.jQuery.fn);
- }
- }
- });
這個(gè)jQuery引用的是博客園網(wǎng)站的,我們沒(méi)有作任何修改。
如果頁(yè)面已經(jīng)預(yù)加載了jquery,運(yùn)行QW.use('jQuery',function(){})也不會(huì)再次加載jquery.js。
當(dāng)然,用戶還可以保持習(xí)慣,用以前的預(yù)加載經(jīng)典用法。
對(duì)于第三條合并請(qǐng)求與并行請(qǐng)求,QW目前還沒(méi)做,不過(guò),只需調(diào)整一下module.h.js里的某一段代碼即可。在源代碼里有說(shuō)明:
- function loadsJsInOrder() {
- //瀏覽器不能保證動(dòng)態(tài)添加的ScriptElement會(huì)按順序執(zhí)行,所以人為來(lái)保證一下
- //參見(jiàn):http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/
- //測(cè)試幫助:http://1.cuzillion.com/bin/resource.cgi?type=js&sleep=3&jsdelay=0&n=1&t=1294649352
- //todo: 目前沒(méi)有充分利用部分瀏覽器的并行下載功能,可以改進(jìn)。
- //todo: 如果服務(wù)器端能combo,則可修改以下內(nèi)容以適應(yīng)。
- 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()) { //如果有l(wèi)oaderChecker,則用loaderChecker判斷一下是否已經(jīng)加載過(guò)
- loadedDone();
- } else {
- loadJs(moduleI.url.replace(/^\/\//, QW.PATH), loadedDone);
- }
- }
- }
對(duì)于第四條緩存,由于是統(tǒng)一配置,所以只需在發(fā)布時(shí),在配置文件的js后面加上對(duì)應(yīng)js的md5碼的前N位即可。
例如,線上的配置可能是:
- Ajax: {
- url: '//components/ajax/ajax.youa.js?111111.js',
- requires: 'YouaCore'
- },
- Anim: {
- url: '//components/animation/anim.js?222222.js',
- requires: 'YouaCore'
- }
頁(yè)面引用的種子,也是在后面加上它的md5碼的前N位。
這樣做的好處是:每次上線后,用戶也只需下載有過(guò)修改的js。
對(duì)于第五條的復(fù)合模塊問(wèn)題,由于是統(tǒng)一配置,所以根本就不是問(wèn)題。
說(shuō)了seed_youa.js的這么多好處,當(dāng)然還要說(shuō)明一個(gè)限制:項(xiàng)目中需要有一個(gè)水平還過(guò)得去的js同學(xué),來(lái)負(fù)責(zé)這個(gè)統(tǒng)一的模塊配置文件。
關(guān)于module.h.js還有挺多內(nèi)容要講的,不過(guò),絕大同學(xué)不用關(guān)心它的實(shí)現(xiàn);關(guān)心他的實(shí)現(xiàn)的同學(xué)大多也看過(guò)NK行的YUI3種子,再看這個(gè)兩百行的module.h.js,應(yīng)該是小case,這里先略過(guò)。
小結(jié)一下,QWrap的這個(gè)種子應(yīng)用是綠色版的,并且小巧、靈活,可以直接復(fù)制過(guò)去引用。不過(guò)需要注意一下你所放的路徑,防止QW.PATH的值計(jì)算有誤,或者強(qiáng)制改掉QW.PATH的值。
附:QWrap網(wǎng)址:http://www.qwrap.com
原文:http://www.cnblogs.com/jkisjk/archive/2011/04/15/qwrap_apps_seed_youa.html
【編輯推薦】