首頁  >  文章  >  web前端  >  使用javascript開發需知

使用javascript開發需知

伊谢尔伦
伊谢尔伦原創
2016-11-22 11:38:41806瀏覽

開篇概述

Ajax技術在Gmail中的成功應用,和高性能的V8引擎的推出,使得編寫Web應用變得流行起來,使用前端技術也可以編寫具有復雜交互的應用。相對於原生應用,Web應用程式具有以下優點:

跨平台,開發和維護成本低;

升級和發布方便,沒有版本的概念,隨時隨地發布,用戶沒有感知,不需要安裝;

響應式設計(Responsive Design)讓Web應用可以跨平台,同一份程式碼自適應各種螢幕大小

即使最終不採用Web應用方案,也很適合開發原型

當然,Web應用也不是沒有缺點。由於不同平台和廠商的瀏覽器並不完全一樣,跨平台也有一些相容成本。另外,Web應用的效能不如native應用,互動有時候不是很流暢, 再加上HTML5的API上的限制,讓有些功能採用Web應用不太適合。由於這些原因,結合兩者優點的混合方案變得流行起來(例如微信、手機QQ和手機QQ瀏覽器中會嵌入一 些Web頁面)。

根據筆者的開發經驗,以下總結一些Web應用開發過程中的要面臨的幾個問題。

模組化程式設計

模組化程式設計是編寫大規模應用必不可少的一個特性,與其它主流的程式語言相比Javascript沒有對模組提供直接的支持,更不用說維護模組之間的依賴關係,這使得維護Javascript程式碼變得異常困難,在<script>標籤中包含程式碼的順序需要人工維護。 </script>

要支援模組化程式必須解決兩個問題:

支援編寫模組並為模組命名,防止名字衝突和全域變數的使用;

支援顯示指定模組之間的依賴關係,並在程式執行時自動加載依賴的模組。

Douglas Crockford在」Javascript: The Good Parts」一書中提出的Module Pattern利用Javascript的閉包技術來模擬模組的概念,防止名字衝突和全域變數的使用。這解決了第一個問題。

var moduleName = function () {
    // Define private variables and functions
    var private = ...
    // Return public interface.
    return {
        foo: ...
    };
}();

為了解決第二個問題CommonJS組織定義了 AMD規範方便 開發者顯示指定模組之間的依賴關係,並在需要時載入依賴的模組。 RequireJS是AMD規範的一個比較流行的實作。

首先我們在a.js中定義模組A.

define(function () {
    return {
        color: "black",
        size: 10
    };
});

然後定義模組B依賴模組A.

define(["a"], function (A) {
    // ...
});

當模組B執行時RequireJS保證模組A已被載入。具體細節可參考RequireJS官方文件。

腳本載入

最簡單的腳本載入方式是放在

載入。
<head>
    <script src="base.js" type="text/javascript"></script>
    <script src="app.js" type="text/javascript"></script>
</head>

其缺點是:

載入和解析是順序是同步執行的,先下載base.js然後解析和執行,然後再下載 app.js;

載入腳本時還會阻塞對<script>之後的DOM元素的渲染。 </script>

為了緩解這些問題,現在的普遍做法是將<script>放在<body>的底部。 </script>

  <script src="base.js" type="text/javascript"></script>
    <script src="app.js" type="text/javascript"></script>
</body>

但並不是所有的腳本都可以放在

的底部,例如有些邏輯要在頁面渲染時執行, 不過大多數腳本沒有這樣的要求。

將腳本放在

底部仍然沒有解決順序下載的問題,一些瀏覽器廠商也意識到了 這個問題並開始支援非同步下載。 HTML5也提供了標準的解決方案:
<script src="base.js" type="text/javascript" async></script>
<script src="app.js" type="text/javascript" async></script>

標上async屬性的腳本表示你沒有在裡面使用document.write之類的程式碼。瀏覽器 將非同步下載和執行這些腳本,並且不會組織DOM樹的渲染。但這會導致另一個問題: 由於是非同步執行,app.js可能在base.js之前執行,如果它們之間有依賴關係這將導致錯誤。

講到這裡從開發者角度來看我們其實需要的是這些特性:

異步下載,不要阻塞DOM的渲染;

按照模組的依賴關係解析和執行腳本。

所以腳本的載入其實需要與模組化程式設計問題結合起來解決。 RequireJS不僅記錄了模 區塊之間的依賴關係,並且提供了根據依賴關係的按需載入和執行(詳情請參考 RequireJS官方文件)。

關於腳本載入的更多方案請看 這裡.

靜態資源檔案的部署

這裡的靜態資源檔案是指CSS、Javascript和CSS需要的一些圖片檔案。它們的部署需 要考慮兩個問題:

下載速度

版本管理

靜態資源檔案的一個特點變更不頻繁,且與使用者身分無關(即與Cookie無關),因此很適合快取。另一方面,一旦靜態資源檔案變更時,瀏覽器必須從網路伺服器下載最新 的版本。當發布新版本的網路應用程式時,並不是所有使用者馬上就用上新版本,舊版和新版本將會共存,這就牽涉到版本比對問題。舊版的應用程式需要下載舊版的CSS和 Javascript,新版本的應用程式需要下載新版本的靜態資源。

為了防止版本不一致,每當發布新版本的應用時靜態資源文件都需要改名,讓舊的 HTML引用舊的靜態文件,新的HTML引用新的靜態文件。一個常見辦法就是在檔案名稱 中加上時間戳記;

為了防止懸掛引用,資源檔案應該比HTML先發布。

上述方案可以解決版本問題,這樣每個靜態檔案的快取時間可以設定得任意大,防止重複下載,同時在新版本發佈時瀏覽器將及時更新。

為解決下載速度問題,可以考慮以下幾個方案:

合併靜態檔案以免檔案數量過多,過多的檔案需要更多的連線來下載,瀏覽器通常對同一個網域的連線數量有限制;

壓縮靜態檔案;為了可讀性,CSS和Javascript通常有很多空行、縮排和註釋,這些在發佈時都可以去掉;

靜態檔案通常與Cookie沒有關係,所以為了減小傳輸大小和增加快取命中率(快取的key需要考慮Cookie),靜態檔案最好託管在沒有Cookie的網域上;

最後也是最重要的,要使上述流程自動化。

MVC程式設計模型

Web應用程式採用的是事件驅動程式設計模型,與native應用是相同的,差異僅在於基礎架構提供的API不一樣。 UI程式設計通常採用MVC設計模式,以流行的 Backbone.js為例包含以下部分:

Model

資料的唯一來源

負責取得和儲存資料

可提供快取機制

資料變更時透過事件通知其它物件

View

負責渲染

監聽UI事件和Model事件並重繪UI

渲染結果取決於兩類資料:Model和UI互動狀態

UI的互動狀態通常存在View物件中,有時為了存在View物件中,有時為了存在View方便也存在DOM樹節點中

為了降低渲染成本,盡量減少需要渲染的區域,每次當資料變化時只渲染受影響的區域

Router

負責監聽URL的變化,並通知對應的View對象渲染頁

為了有效地使用MVC,有幾個問題要注意。

Model應與View完全隔離

Model僅提供資料的訪問​​,不應該依賴View,因此Model不應該知道View的存在。所以 Model不能持有對任何View物件的引用。 Model的資料變更時只能透過事件通知View.

View在初始化時採用委派方式監聽UI事件

這裡有兩個關鍵點:

在初始化時監聽事件var View = Backbone.View.extend( { initialize: function () { this.$el.on('click', '#id', function () { // … }); } });

除了一些特殊情況外(見下文),所有UI事件都應該在View初始化時初始化,防止同一個事件被綁定多次。即使有些事件是動態監聽的(有時候需要監聽,有時候有不需要監聽,例如有些按鈕有時候是有效的,有時又無效),也需要在初始化時監聽,然後在事件回呼函數裡判斷是否需要處理。這樣邏輯更簡單,也更容易維護。

採用委派方式監聽UI事件

關於委派方式監聽請參考jQuery文檔.

上面已強調要在初始化時監聽事件,但是初始化時需要監聽的DOM節點可能還不存在, 所以沒法直接綁定事件,只能採用委派方式。不過採用委派方式要求事件可以冒泡。

對於那些沒法冒泡的事件(例如使用javascript開發需知的load事件)只能在保證其存在的情況下直 接綁定,而不一定要在初始化時綁定。

複雜的View組織成樹形層次結構

函數太大了需要拆分成幾個子函數。同樣,View的邏輯如果過於複雜也應根據頁面結構拆成幾個子View:

父View透過引用訪問子View,但是子View不應該持有父View的引用;

子View只負責自己區域的渲染,其它區域由父View負責渲染;

父View透過函數呼叫存取子View的功能,子View透過事件與父View通訊;

子View之間無法直接通訊。

其它技巧可查看 Backbone技巧與模式.

離線應用緩存

為使Web應用體驗更加流暢,可考慮使用HTML5離線應用緩存,不過有以下幾點需要注意:

不要將離線應用緩存與HTTP快取機制搞混淆,前者是HTML5引入的新特性,與HTTP快取機制是相互獨立並存的;

Cache manifest文件不应被HTTP缓存太久(通过HTTP头Cache-Control控制缓存 时间),否则发布新版后浏览器不会及时监测到变化并下载新文件;

在Cache manifest文件的NETWORK节放一个*,否则没有列在这个文件的资源不 会被请求;

不适合缓存的请求最好都放在NETWORK节;

如果之前使用过离线应用缓存现在不想再使用了,从100db36a723c770d327fc0aef2ce13b1删除manifest属性, 并发送404响应给manifest文件请求。仅仅删除manifest属性是没有效的。

线上错误报告

Javascript是一个动态语言,许多检查都是在运行时执行的,所以大多数错误只有执行到的时候才能检查到,只能在发布前通过大量测试来发现。即使这样仍可能有少数 没有执行到的路径有错误,这只能通过线上错误报告来发现了。

window.onerror = function (errorMsg, fileLoc, linenumber) {
    var s = &#39;url: &#39; + document.URL + &#39;\nfile: &#39; + fileLoc
       + &#39;\nline number: &#39; + linenumber
       + &#39;\nmessage: &#39; + errorMsg;
    Log.error(s); // 发给服务器统计监控
    console.log(s);
};

通常线上的Javascript都是经过了合并和压缩的,上报的文件名和行号基本上没法对 应到源代码,对查错帮助不是很大。不过最新版的Chrome支持在onerror的回调函数 中获取出错时的栈轨迹:window.event.error.stack.


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn