Web應(yīng)用開發(fā)中的幾個問題Introduction由于Ajax技術(shù)在Gmail中的成功應(yīng)用和高性能的V8引擎的推出使得編寫Web應(yīng)用變得流行 起來,使用前端技術(shù)也可以編寫具有復(fù)雜交互的應(yīng)用。相對于native應(yīng)用,Web應(yīng)用具 有如下優(yōu)點:
當(dāng)然,Web應(yīng)用也不是沒有缺點。由于不同平臺和廠商的瀏覽器并不完全一樣,跨平臺 也有一些兼容成本。另外,Web應(yīng)用的性能不如native應(yīng)用,交互有時候不是很流暢, 再加上HTML5的API上的限制,使得有些功能采用Web應(yīng)用不太合適。由于這些原因,結(jié) 合兩者優(yōu)點的混合方案變得流行起來(比如微信、手機(jī)QQ和手機(jī)QQ瀏覽器中會嵌入一 些Web頁面)。 根據(jù)筆者的開發(fā)經(jīng)驗,下面總結(jié)一些Web應(yīng)用開發(fā)過程中的要面臨的幾個問題。 模塊化編程模塊化編程是編寫大規(guī)模應(yīng)用必不可少的一個特性,與其它主流的編程語言相比 Javascript沒有對模塊提供直接的支持,更不用說維護(hù)模塊之間的依賴關(guān)系,這使得維 護(hù)Javascript代碼變得異常困難,在<script>標(biāo)簽中包含代碼的順序需要人工維護(hù)。 要支持模塊化編程必須解決兩個問題:
Douglas Crockford在”Javascript: The Good Parts”一書中提出的Module Pattern利 用Javascript的閉包技術(shù)來模擬模塊的概念,防止名字沖突和全局變量的使用。這解 決了第一個問題。 var moduleName = function () {
// Define private variables and functions
var private = ...
// Return public interface.
return {
foo: ...
};
}();
為了解決第二個問題CommonJS組織定義了 AMD規(guī)范方便 開發(fā)者顯示指定模塊之間的依賴關(guān)系,并在需要時加載依賴的模塊。 RequireJS是AMD規(guī)范的一個比較流行的實現(xiàn)。 首先我們在a.js中定義模塊A. define(function () {
return {
color: "black",
size: 10
};
});
然后定義模塊B依賴模塊A. define(["a"], function (A) {
// ...
});
當(dāng)模塊B執(zhí)行時RequireJS保證模塊A已被加載。具體細(xì)節(jié)可參考RequireJS官方文 檔。 腳本加載最簡單的腳本加載方式是放在<head>加載。 <head> <script src="base.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> </head> 其缺點是:
為了緩解這些問題,現(xiàn)在的普遍做法是將<script>放在<body>的底部。 <script src="base.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> </body> 但并不是所有的腳本都可以放在<body>的底部,比如有些邏輯要在頁面渲染時執(zhí)行, 不過大多數(shù)腳本沒有這樣的要求。 將腳本放在<body>底部仍然沒有解決順序下載的問題,一些瀏覽器廠商也意識到了 這個問題并開始支持異步下載。HTML5也提供了標(biāo)準(zhǔn)的解決方案: <script src="base.js" type="text/javascript" async></script> <script src="app.js" type="text/javascript" async></script> 標(biāo)上async屬性的腳本表明你沒有在里面使用document.write之類的代碼。瀏覽器 將異步下載和執(zhí)行這些腳本,并且不會組織DOM樹的渲染。但是這會導(dǎo)致另一個問題: 由于是異步執(zhí)行,app.js可能在base.js之前執(zhí)行,如果它們之間有依賴關(guān)系這將 導(dǎo)致錯誤。 講到這里從開發(fā)者角度來看我們其實需要的是這些特性:
所以腳本的加載其實需要與模塊化編程問題結(jié)合起來解決。RequireJS不僅記錄了模 塊之間的依賴關(guān)系,并且提供了根據(jù)依賴關(guān)系的按需加載和執(zhí)行(詳情請參考 RequireJS官方文檔)。 關(guān)于腳本加載的更多方案請看 這里. 靜態(tài)資源文件的部署這里的靜態(tài)資源文件是指CSS、Javascript和CSS需要的一些圖片文件。它們的部署需 要考慮兩個問題:
靜態(tài)資源文件的一個特點變化不頻繁,且與用戶身份無關(guān)(即與Cookie無關(guān)),因此 很適合緩存。另一方面,一旦靜態(tài)資源文件變化時,瀏覽器必須從Web服務(wù)器下載最新 的版本。當(dāng)發(fā)布新版本的Web應(yīng)用時,并不是所有用戶馬上就用上新版本,老版本和新 版本將會共存,這就涉及到版本匹配問題。老版本的應(yīng)用需要下載老版本的CSS和 Javascript,新版本的應(yīng)用需要下載新版本的靜態(tài)資源。
上述方案可以解決版本問題,這樣每個靜態(tài)文件的緩存時間可以設(shè)置得任意大,防止 重復(fù)下載,同時在新版本發(fā)布時瀏覽器將及時更新。 為解決下載速度問題,可以考慮以下幾個方案:
最后也是最重要的,要使上述過程自動化。 MVC編程模型Web應(yīng)用采用的是事件驅(qū)動編程模型,與native應(yīng)用是一樣的,區(qū)別僅在于基礎(chǔ)設(shè)施提 供的API不一樣。UI編程通常采用MVC設(shè)計模式,以流行的 Backbone.js為例包括如下部分:
為了有效地使用MVC,有幾個問題需要注意。 Model應(yīng)與View完全隔離Model僅提供數(shù)據(jù)的訪問,不應(yīng)該依賴View,因此Model不應(yīng)該知道View的存在。所以 Model不能持有對任何View對象的引用。Model的數(shù)據(jù)發(fā)生變化時只能通過事件通知 View. View在初始化時采用委派方式監(jiān)聽UI事件這里有兩個關(guān)鍵點:
除了一些特殊情況外(請看下文),所有UI事件都應(yīng)該在View初始化時初始化,防止同 一個事件被綁定多次。即使有些事件是動態(tài)監(jiān)聽的(有時候需要監(jiān)聽,有時候有不需要 監(jiān)聽,比如有些按鈕有時候是有效的,有時候又無效),也需要在初始化時監(jiān)聽,然后 在事件回調(diào)函數(shù)里判斷是否需要處理。這樣邏輯更簡單,更容易維護(hù)。
關(guān)于委派方式監(jiān)聽請參考jQuery文檔. 上面已強(qiáng)調(diào)要在初始化時監(jiān)聽事件,但是初始化時需要監(jiān)聽的DOM節(jié)點可能還不存在, 所以沒法直接綁定事件,只能采用委派方式。不過采用委派方式要求事件可以冒泡。 對于那些沒法冒泡的事件(比如<img>的load事件)只能在保證其存在的情況下直 接綁定,而不一定要在初始化時綁定。 復(fù)雜的View組織成樹形層次結(jié)構(gòu)函數(shù)太大了需要拆分成幾個子函數(shù)。同樣,View的邏輯如果過于復(fù)雜也應(yīng)根據(jù)頁面結(jié) 構(gòu)拆成幾個子View:
其它技巧可查看 Backbone技巧與模式. 離線應(yīng)用緩存為使Web應(yīng)用體驗更加流暢,可考慮使用HTML5離線應(yīng)用緩存,不過有以下幾點需要注 意:
線上錯誤報告Javascript是一個動態(tài)語言,許多檢查都是在運行時執(zhí)行的,所以大多數(shù)錯誤只有執(zhí) 行到的時候才能檢查到,只能在發(fā)布前通過大量測試來發(fā)現(xiàn)。即使這樣仍可能有少數(shù) 沒有執(zhí)行到的路徑有錯誤,這只能通過線上錯誤報告來發(fā)現(xiàn)了。 window.onerror = function (errorMsg, fileLoc, linenumber) {
var s = 'url: ' + document.URL + '\nfile: ' + fileLoc
+ '\nline number: ' + linenumber
+ '\nmessage: ' + errorMsg;
Log.error(s); // 發(fā)給服務(wù)器統(tǒng)計監(jiān)控
console.log(s);
};
通常線上的Javascript都是經(jīng)過了合并和壓縮的,上報的文件名和行號基本上沒法對 應(yīng)到源代碼,對查錯幫助不是很大。不過最新版的Chrome支持在onerror的回調(diào)函數(shù) 中獲取出錯時的棧軌跡:window.event.error.stack. Web應(yīng)用開發(fā)中的幾個問題,首發(fā)于博客 - 伯樂在線。 |
|
|