前面的章節皆在探討 JavaScript 核心,與執行環境無關;本篇則著重於 browser 上的相關 pattern。

Separation of Concerns

  • Concerns 有三:
    1. Context:文件內容,即 HTML
    2. Presentation:外觀表現,即 CSS
    3. Behavior:使用者互動或文件動態改變,即 JavaScript
  • 當關閉 CSS 或 JavaScript 時,應確保頁面仍可正常閱讀,且主要功能仍可正常運作
  • 避免將 CSS 或 JavaScript 內嵌在 context 中
  • 應直接檢測明確的 method 或 property,避免使用 user agent sniffing

DOM Scripting

  • 避免在 loop 中存取 DOM,應 assign 其 reference 給 local variable
  • 盡量使用 selector API(如 document.querySelector()document.querySelectorAll()),而 id="" 是尋找節點最快的方式(document.getElementById()
  • 更新 DOM 會使 browser 進行 repain(重新繪製畫面)及 reflow(重新計算元素的幾何結構),因此應在 “live” DOM tree 之外進行操作(如:新增時先用 document fragment 容納所有節點,待一連串的動作完成後再將這個已定案的 fragment 加至 DOM;修改時則可先在 clone 中處理,完成後再替換原本的節點)

Events

  • 使用 addEventListener()/attachEvent() 時,適時加上 stopPropagation()/cancelBubble=truepreventDefault()/returnValue=false
  • 利用 event delegation 減少 event listeners 的總量(如 div 內有十個 button,可在 div 上加一個 listener 就好而不是在每一個 button 都加)

Long-Running Scripts

  • JavaScript 中雖沒有 thread,但可藉由 setTimeOut() 模擬,或使用較新的 browser 所支援的 Web Workers,以避免因 lag 而使 user 不能操作畫面

Remote Scripting

  • XMLHttpRequest (AJAX)
  • JSONP (for cross-domain)
  • iframe
  • image beacon(只需送 request 不需接 response 時可用,response 傳 HTTP 204 為佳)

Deploying JavaScript

  • 合併 scripts 以減少 HTTP request,可用 cat 命令操作
  • 使用 minify 工具壓縮,縮小檔案大小
  • 開啟 gzip 壓縮,增快載入速度
.htaccess
1
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml application/javascript application/json
  • 使用 Expires header,增加檔案待在 cache 的機會
.htaccess
1
2
ExpiresActive On
ExpiresByType application/x-javascript "access plus 10 years"
  • 使用 CDN,讓各地 user 可更快速存取檔案

Loading Strategies

  • <script> 的 attribute 不要加上 languagetype 等歷史遺跡,可加 defer 或 HTML5 的 async 使其不會 block 網頁的下載流程(browser 會同時下載多個檔案,但遇到外部 script 會暫停下一步下載直到該 script 完成下載並分析與執行完畢);async 尚未廣泛支援,因此可將外部 script 放置頁面底端(</body> 前),或動態加入 <script> 元素實現 asyncronous 下載(document.createElement("script")),後者需注意各 script 檔之間的相依性(因各檔案下載完成的時機未必如同預期順序)
  • 當頁面需在 server-side 進行複雜運算時,可使用 chunked encoding 分批傳送頁面片段,利用 server 尚未完成工作時先將部分靜態內容送給 client-side

Strategy of Loading a Big Script

  • lazy loading:先載入 core 使主要功能開始運作,再用 onload 動態加 <script> 載入其他次要功能(如特效、XHR 實作等)
  • loading on demand:需要時才載入,並在完成後執行 callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require("extra.js", function () {
functionDefinedInExtraJS();
});
function require(file, callback) {
var script = document.getElementsByTagName('script')[0],
newjs = document.createElement('script');
// IE
newjs.onreadystatechange = function () {
if (newjs.readyState === 'loaded' || newjs.readyState === 'complete') {
newjs.onreadystatechange = null;
callback();
}
};
// others
newjs.onload = function () {
callback();
};
newjs.src = file;
script.parentNode.insertBefore(newjs, script);
}
  • preloading:若希望單純只下載 script 檔案而不進行分析及執行,IE 可用 image beacon,其他 browser 可用 <object> 替代 <script>