雛鳳清音——面向數(shù)據(jù)的前端編程方法
1 名詞解釋
桐花萬里丹山路,雛鳳清于老鳳聲——唐·李商隱《韓冬郎既席為詩相送因成二絕》
作為一種有別傳統(tǒng)的前端編程方法,雛風(fēng)名之。
面向數(shù)據(jù)的編程方法,避開繁瑣的ui代碼,直接針對(duì)前端數(shù)據(jù)模型編程,你的程序就能更加清晰簡(jiǎn)單。清音名之。
2 從實(shí)例開始
避免過于枯燥的陳述,我們從實(shí)例開始,我們以百度 WebIM分組管理為例。
這里面,我們需要對(duì)用戶分組列表執(zhí)行:添加、刪除、修改、保存、取消、排序等六種功能。
這算是一個(gè)比較常見的需求,那么,我們通常在前段實(shí)現(xiàn)這種功能,一般如何設(shè)計(jì)?多少代碼?多長(zhǎng)時(shí)間?
思考時(shí)間……………………….
===================================================
好了現(xiàn)在我們來展示一中簡(jiǎn)單的實(shí)現(xiàn)方式:
程序代碼:
var?data?=?{ ? ??"list":?[ ? ??{ ? ? ? ??"id":?1, ? ? ? ??"name":?"我的分組1" ? ??}, ? ??{ ? ? ? ??"id":?2, ? ? ? ??"name":?"我的分組2" ? ??}, ? ??{ ? ? ? ??"id":?3, ? ? ? ??"name":?"我的分組3" ? ??}, ? ??{ ? ? ? ??"id":?4, ? ? ? ??"name":?"我的分組4" ? ??} ? ??] } var?action?=?{ ? ? sort:?function(list,?inc)?{ ? ? ? ? $("sort").className?=?inc???"down":?"up"; ? ? ? ? list.sort(function(a,?b)?{ ? ? ? ? ? ??return?(inc???1:?-1)?*?a.name.localeCompare(b.name); ? ? ? ??}); ? ? ? ? render(data); ? ??}, ? ? create:?function(name)?{ ? ? ? ? data.list.push({ ? ? ? ? ? ? id:?+new?Date(), ? ? ? ? ? ??name:?name ? ? ? ??}); ? ? ? ? render(data); ? ??}, ? ? edit:?function(id)?{ ? ? ? ? each(data.list, ? ? ? ??function(value,?i)?{ ? ? ? ? ? ? data.list[i].state?=?value.id?==?id???"edit":?"normal"; ? ? ? ??}); ? ? ? ? render(data); ? ??}, ? ? del:?function(id)?{ ? ? ? ? each(data.list, ? ? ? ??function(value,?i)?{ ? ? ? ? ? ??if?(value.id?==?id)?{ ? ? ? ? ? ? ? ? data.list.splice(i,?1); ? ? ? ? ? ??} ? ? ? ??}) ? ? ? ? render(data); ? ??}, ? ? save:?function(id)?{ ? ? ? ? each(data.list, ? ? ? ??function(value,?i)?{ ? ? ? ? ? ??if?(value.id?==?id)?{ ? ? ? ? ? ? ? ? value.name?=?$("g_"?+?id).value; ? ? ? ? ? ? ? ? value.state?=?"normal"; ? ? ? ? ? ??} ? ? ? ??}); ? ? ? ? render(data); ? ??}, ? ? cancel:?function(id)?{ ? ? ? ? each(data.list, ? ? ? ??function(value,?i)?{ ? ? ? ? ? ? data.list[i].state?=?"normal"; ? ? ? ??}); ? ? ? ? render(data); ? ??} } function?$(id)?{ ? ??return?document.getElementById(id); } function?each(obj,?fn)?{ ? ??for?(var?i?=?0;?i?<?obj.length;?i++)?{ ? ? ? ? fn.call(obj[i],?obj[i],?i); ? ??} } function?render(data)?{ ? ? $("container").innerHTML?=?teamList(data); }
模板代碼:
<div class="${item.state}"> <div class="cell1"> <input id="g_${item.id}" value="${item.name}" /></div> <div class="cell2"> <button onclick="action.submit(${item.id})"> 提交 </button></div> <div class="cell3"> <button onclick="action.cancel(${item.id})"> 取消 </button></div> <div class="cell1">${item.name}</div> <div class="cell2"> <img onclick="action.edit(${item.id})" src="images/edit.gif" alt="" /></div> <div class="cell3"> <img onclick="action.del(${item.id})" src="images/delete.gif" alt="" /></div> </div>
以傳統(tǒng)的編程方法不同,我們沒有直接操作html,或者說文檔對(duì)象模型,而是抽象出一個(gè)更加簡(jiǎn)單的數(shù)據(jù)模型–一個(gè)JavaScript 數(shù)組對(duì)象。
我們所有的操作都是從這個(gè)簡(jiǎn)單的原始數(shù)據(jù)開始,數(shù)據(jù)模型被修改后,調(diào)用模板渲染函數(shù),重新前端局部刷新展現(xiàn)UI。操作即可完成。
我們所有的操作都是從這個(gè)簡(jiǎn)單的原始數(shù)據(jù)開始,數(shù)據(jù)模型被修改后,調(diào)用模板渲染函數(shù),重新前端局部刷新展現(xiàn)UI。操作即可完成。
這種方法能給我們簡(jiǎn)化多少代碼呢?大家可以去翻翻那些比較出名的JavaScript圖書,里面通常會(huì)花一個(gè)小節(jié)的篇幅去講解一個(gè)話題,那就是表格排序。
對(duì)照這里的表格排序,7行代碼,需要一個(gè)章節(jié)去解釋嗎?
3 核心思想
3.1 一切從源頭開始
傳統(tǒng)的編程方法有一個(gè)問題,那就是他修改的對(duì)象是文檔對(duì)象模型,但是,當(dāng)您的程序足夠復(fù)雜的時(shí)候,這種文檔對(duì)象模型往往不夠簡(jiǎn)單,有太多冗余,或者夾雜著太多的靜態(tài)展示邏輯,我們控制它非常麻煩。好了,我們沒有必要遷就文檔對(duì)象模型,我們要設(shè)計(jì)更加簡(jiǎn)單的沒有冗余的前端模型。
一切從源頭開始,不僅是為了操作上更加簡(jiǎn)單,更重要的一點(diǎn)是,避免數(shù)據(jù)不一致和錯(cuò)誤累加的危害。
如果是傳統(tǒng)的文檔數(shù)據(jù)模型,不僅他本身帶有大量冗余。我們前段操作代碼也容易有大量的冗余。如上例,傳統(tǒng)的方法中,初次展現(xiàn)的分組列表和后來編輯,添加的分組,可能生成方法都不一樣,分布在不同的代碼中,一單涉及到展現(xiàn)風(fēng)格的修改,都得多次同步編輯,這在編程上的大忌。而我們上面介紹的方法中,我們只有一處展現(xiàn)邏輯,那就是完整覆蓋。 仍外,傳統(tǒng)方法是對(duì)UI做增量的修改操作,但是加加減減的多了,難免不出點(diǎn)小問題,而且,這種問題只能被累加起來,但是在我們新的編程方法中,因?yàn)槲覀兠看味际钦w覆蓋,這樣就不會(huì)有錯(cuò)誤累加的問題了。
3.2 表現(xiàn)層沒有上下文
既然我們有自己的數(shù)據(jù)模型,就不要隨便在文檔對(duì)象模型上保留任何狀態(tài)了,任何修改,都反映到數(shù)據(jù)模型中去吧,狀態(tài)改變了,同步數(shù)據(jù)模型,再?gòu)臄?shù)據(jù)模型開始,完全覆蓋前端展示。
4 性能的問題
上例中的聯(lián)系人分組管理,能承受多大的數(shù)據(jù)呢?
我們得先解釋一下我們的統(tǒng)計(jì)方法。
人的視覺停留時(shí)間是0.1秒,或者說100毫秒。也就是說,100毫秒間隔人類是無法感知的。而我們的網(wǎng)頁操作。是一種延遲,不是間隔,人類能感知的時(shí)間延遲有多少呢?我們沒有權(quán)威的數(shù)據(jù),一般認(rèn)為是300毫秒。300毫秒內(nèi)的延遲,我們是無法感知的。
那么300毫秒內(nèi),我們能渲染多少數(shù)據(jù)呢?IE8,F(xiàn)F3(不打開Firebug,打開firebug能渲染400次)上能支持1000條左右,Chrome上能支持3000次左右。
另外,在某些情況下,面向數(shù)據(jù)的編程方法,性能可能還具有一定的優(yōu)勢(shì)。比如,百度工具欄widget列表的設(shè)計(jì)中,我們一次裝載了較多的widget數(shù)據(jù),但是我們每次只展示一小部分,這樣,面向數(shù)據(jù)邊車呢個(gè)方法中DOM節(jié)點(diǎn)明顯比傳統(tǒng)方式少一個(gè)數(shù)量級(jí)。要知道,DOM節(jié)點(diǎn)是非常消耗內(nèi)存的。。。。
說點(diǎn)不足
動(dòng)畫,對(duì),我們不應(yīng)該采用這種方式做網(wǎng)頁動(dòng)畫,因?yàn)閯?dòng)畫的刷性要求太高,模板這種重頭開始的方式不再適用。
所以,真要使用js動(dòng)畫效果的時(shí)候,你還是應(yīng)腳本庫去輔助。
同時(shí)提一點(diǎn)個(gè)人看法,用戶體驗(yàn)的提高并不是動(dòng)畫這樣吸引 眼球的效果。更多考慮應(yīng)該是讓用戶操作更方便,不要被扣分。
5 如何體驗(yàn)?
1. 運(yùn)行JSide調(diào)試服務(wù)器:http://www.xidea.org/webstart/JSide.jnlp
一個(gè)webstart程序,JSide 啟動(dòng)之后會(huì)在窗口右側(cè)出現(xiàn)一個(gè)彩色的浮層(無邊框窗口,如果是Java6u10+,這個(gè)窗口會(huì)透明顯示)
2. 下載測(cè)試程序并解壓。?http://lite.googlecode.com/files/Example-20091219.zip
下載之后隨便解壓縮到一個(gè)目錄里面吧。
3. 設(shè)置調(diào)試網(wǎng)站目錄。
測(cè)試服務(wù)器就是為了測(cè)試方便而設(shè)計(jì)的,你只要把剛才解壓縮的目標(biāo)目錄拖放到JSide浮層上,測(cè)試服務(wù)器自動(dòng)切換網(wǎng)站目錄。
4. 查看測(cè)試程序。
每次切換網(wǎng)站目錄后,程序會(huì)提示您打開網(wǎng)站首頁,你打開就是了。首頁默認(rèn)是一個(gè)文件列表。選擇你看著順眼的文件點(diǎn)擊吧(*.s.js是一種用JavaScript編寫的服務(wù)端小程序,可以在JSide測(cè)試服務(wù)器上運(yùn)行)
5.修改程序代碼。
在JSide浮層上右鍵,瀏覽文件,修改吧。
來源:http://www.baiduux.com/blog/2010/01/14/data_oriented_programing_in_web_frontend/
- 目前還沒評(píng)論,等你發(fā)揮!