優化 RequireJS 專案(合併與壓縮)

發表於2013-05-06

英文原文:Optimize (Concatenate and Minify) RequireJS Projects,編譯:oschina

本文將演示如何合併與壓縮一個基於RequireJS的專案。本文中將用到苦幹個工具,這其中就包括Node.js。 因此,如果你手頭上還沒有Node.js可以點選此處下載一個。

動機

關於RequireJS已經有很多文章介紹過了。這個工具可以將你的JavaScript程式碼輕易的分割成苦幹個模組(module)並且保持你的程式碼模組化與易維護性。這樣,你將獲得一些具有互相依賴關係的JavaScript檔案。僅僅需要在你的HTML文件中引用一個基於RequireJS的指令碼檔案,所有必須的檔案都將會被自動引用到這個頁面上.

但是,在生產環境中將所有的JavaScript檔案分離,這是一個不好的做法。這會導致很多次請求(requests),即使這個些檔案都很小,也會浪費很多時間。 可以通過合併這些指令碼檔案,以減少請求的次數達到節省載入時間的目的。

另一種節省載入時間的技巧是縮小這些被載入檔案的大小,相對小一些的檔案會傳輸的更快一些。這個過程叫作最小化 (minification) ,它是通過小心的改變指令碼檔案的程式碼結構並且不改變程式碼的形為(behavior)和功能(functionality)來實現的。例如這些:去除不必要的空格,縮短(mangling,或都壓縮)變數(variables)名與函式(methods,或者叫方法)名,等等。這種合併並壓縮檔案的過程叫做程式碼優化( optimization)。這種方法除了用於優化(optimization)JavaScript檔案,同樣適用於CSS檔案的優化。

RequireJS有兩個主要方法(method): define()和require()。這兩個方法基本上擁有相同的定義(declaration) 並且它們都知道如何載入的依賴關係,然後執行一個回撥函式(callback function)。與require()不同的是, define()用來儲存程式碼作為一個已命名的模組。 因此define()的回撥函式需要有一個返回值作為這個模組定義。這些類似被定義的模組叫作AMD (Asynchronous Module Definition,非同步模組定義)。

如果你不大熟悉RequireJS或者不太明白我寫的東西 – 不要擔心。下面有一個關於這些的例子。

JavaScript應用程式的優化

在本小節中我將向大家展示如何優化Addy Osmani的TodoMVC Backbone.js + RequireJS 專案。 由於TodoMVC專案在不同的框架下包含許多TodoMVC實現,我下載了1.1.0版並提取出Backbone.js + RequireJS應用程式。點選這裡下載該應用程式並解壓下載到的zip檔案。todo-mvc的解壓目錄將是我們這個例子的根目錄(root path),從現在起我將把這個目錄引用為<root>。

檢視<root>/index.html的原始碼,你會發現它僅僅包含了一個script標籤(另外一個是當你使用Internet Explorer時引用的):

index.html引用指令碼檔案的程式碼

其實,整個專案只需要引用require.js這個指令碼檔案。如果你在瀏覽器中執行這個專案,並且在你喜歡的(擅長的)除錯工具的network標籤中, 你就會發現瀏覽器同時也載入了其它的JavaScript檔案:

優化 RequireJS 專案(合併與壓縮)
所有在紅線邊框裡面的指令碼檔案都是由RequireJS自動載入的。

我們將用RequireJS Optimizer(RequireJS優化器)來優化這個專案。根據已下載的說明檔案,找到r.js並將其複製到<root>目錄。 jrburke的r.js是一個能執行基於AMD的專案的命令列工具,但更重要的是,它包含RequireJS Optimizer允許我們對指令碼檔案(scripts)合併與壓縮。

RequireJS Optimizer有很多用處。它不僅能夠優化單個JavaScript或單個CSS檔案,它還可以優化整個專案或只是其中的一部分,甚至多頁應用程式(multi-page application)。它還可以使用不同的縮小引擎(minification engines)或者乾脆什麼都不用(no minification at all),等等。本文無意於涵蓋RequireJS Optimizer的所有可能性,在此僅演示它的一種用法。

正如我之前所提到的,我們將用到Node.js來執行優化器(optimizer)。用如下的命令執行它(optimizer):

執行RequireJS Optimizer

有兩種方式可以將引數傳遞給optimizer。一種是在命令列上指定引數:

在命令列上指定引數

另一種方式是構建一個配置檔案(相對於執行資料夾)幷包含指定的引數 :

build.js的內容:配置檔案中的引數

我認為構建一個配置檔案比在命令列中使用引數的可讀性更高,因此我將採用這種方式。接下來我們就為專案建立一個<root>/build.js檔案,並且包括以下的引數: <root>/build.js

弄明白RequireJS Optimizer的所有配置項並不是本文的目的所在,但我想解釋(描述)一下本文中我所採用的引數:

引數 描述
appDir 應用程式的目錄(即<root>)。在這個資料夾下的所有檔案將會被複制到dir引數標註的資料夾下。
baseUrl 相對於appDir,代表查詢檔案的錨點(that represents the anchor path for finding files)。
dir 這是一個輸出目錄,所有的應用程式檔案將會被複制到該資料夾下。
modules 一個包含多個物件的陣列。每個物件代表一個將被優化的模組(module)。
fileExclusionRegExp 任何與此規則匹配的檔案或資料夾都將不會被複制到輸出目錄。由於我們把r.js和build.js放置在應用程式目錄下,我們希望優化器(optimizer)排除這兩個檔案。 因此我們可以這樣設定/^(r|build)\.js$/。
optimizeCss RequireJS Optimizer會自動優化應用程式下的CSS檔案。這個引數控制CSS最優化設定。允許的值: “none”, “standard”, “standard.keepLines”, “standard.keepComments”, “standard.keepComments.keepLines”。
removeCombined 如果為true,優化器(optimizer)將從輸出目錄中刪除已合併的檔案。
paths 模組(modules)的相對目錄。
shim 為那些沒有使用define()聲名依賴關係及設定模組值的模組,配置依賴關係與“瀏覽器全域性”出口的指令碼。

 

瞭解RequireJS Optimizer的更多介紹以及更多高階應用,除了其網頁早先提供的資料,你可以點選此處查閱所有可用配置選項的詳細的資訊。

既然現在已經有了構建檔案(build file),那麼就可以執行優化器(optimizer)了。進入<root> 目錄並執行如下命令:

執行優化器(optimizer)

一個新的資料夾會被生成:<root>/dist。重要的是要注意到,現在<root>/dist/js/main.js包含了所有已合併與壓縮的具有依賴關係的檔案。 此外,<root>/dist/css/base.css也被優化了。

執行優化後的專案,它看起來與未優化之前的專案完全一樣。再檢查一下該頁面的網路傳輸(network traffic)資訊,會發現僅有兩個JavaScript檔案被載入。

優化 RequireJS 專案(合併與壓縮)

RequireJs Optimizer將伺服器上的指令碼檔案從13個減少到2個並且將檔案的總大小從164KB減少到58.6KB(require.js與main.js)。

開銷

顯然,在優化之後,我們再也沒有必要引用require.js檔案了。因為已經沒有被分離的指令碼檔案了並且所有具有依賴關係的檔案也已被載入。

儘管如此,優化過程將我們所有的指令碼合併生成了一個優化後的指令碼檔案,其中包含了很多次define() 和require()呼叫。 因此,為了保證應用程式能夠正常執行,define()和require()必須指定並實施到應用程式的某處(即包含這些檔案)。

這會導致一個眾所周知的開銷:我們總是會有一些程式碼實現define()和require()。這些程式碼並不是應用程式的一部分,它們的存在僅僅是為我們的基礎建設考慮(infrastructure considerations)。 當我們開發一個JavaScript庫(JavaScript library)時,這個問題變得尤為巨大。相比RequireJS,這些庫通常都很小,因此在庫中包含它會造成一筆巨大的開銷。

在我寫這篇文章的時候,對於這方面的開銷還沒有一個完整的解決方案,但是我們可以使用almond來緩解這個問題。Almond是一個極簡單的AMD載入器,它實現了RequireJS介面(API)。因此,可以用來在已優化過的程式碼中替代RequireJS實現,我們可以在專案中包含almond。
如令,我正致力於開發一個優化器(optimizer),它將能夠優化RequireJS應用程式,而無需開銷,但它仍然是一個新的專案(處於開發的初期階段)因此這裡沒有任何關於它的展示。

下載與總結

  • 下載 未經優化的TodoMVC Backbone.js + RequireJS 專案或者檢視它。
  • 下載 優化後的TodoMVC Backbone.js + RequireJS 專案(位於dist資料夾下)或檢視它。

在閱讀完這篇文章後,我相信你已經明確的知道如何去優化你的RequireJS應用程式。我會很高興的解答您的任何問題。

祝你好運!
NaorYe

相關文章