JavaScript 專案優化總結

吳秦發表於2013-01-23

來源:吳秦的部落格

前段時間對公司已有專案JavaScript程式碼進行優化,本文的是對優化工作的一個總結,拿出來與大家分享。當然我的優化方式可能並不是最優的,或者說有些不對的地方,請指教。

JavaScript優化總結分為以下幾點

優化前後對比

優化前

優化後

程式碼混亂,同樣功能的函式重複出現在多個地方。如果需要修改實現,需要找到所有的地方。牽一髮而動全身 模組化,提取公共介面組織為庫、結構清晰、方便程式碼重用、並且能夠遊戲防止變數汙染問題。
JavaScript檔案未壓縮,size比較大載入消耗網路耗時,阻塞頁面渲染 JavaScript公共庫檔案使用UglifyJS壓縮:
●   Size比較小優化了網路載入時間
●   壓縮混淆了程式碼,在一定程度上保護程式碼
使用時需要載入多個單獨的JavaScript檔案,增加了http請求數降低效能 對公共庫合併壓縮在減少size的同時,減少http請求數
缺乏文件(讓後面的開發者對已有功能不清楚,這在一定程度上造成前面說的,同樣功能的函式重複出現在多個地方) 公共庫中每個類、函式、屬性都有說明文件

●   模組化(類程式設計):程式碼清晰、有效防止變數汙染問題、程式碼重用方便擴充套件等;

●   JavaScript壓縮混淆:減少size優化載入時間,混淆保護程式碼;

●   JavaScript檔案合併:減少http請求優化網路耗時提升效能;

●   生成文件:方便公共庫的使用,查詢介面方便。

 

模組化(類程式設計)

對於靜態類來說JavaScript實現比較簡單,使用Object直接量就已經夠用了;但是要建立例項化、可繼承經典的類需要做一番工作。因為JavaScript是基於原型的(prototype-based程式語言,並沒有包含內建類的實現(它沒有訪問控制符,它沒有定義類的關鍵字class,它沒有支援繼承的extend或冒號,它也沒有用來支援虛擬函式的virtual等),但是我們通過JavaScript可以輕易地模擬出經典的類。

靜態類

根據寶寶JS公共介面的特性,它們不需要例項化,所以優化使用了該方式。下面以PetConfigParser為例介紹下實現方式:

 

這種方式利用了JavaScript匿名函式來建立私有作用域,這些私有作用域只能在內部訪問。總結上述過程分為以下幾個步驟:

1)        定義一個全域性的變數(var PetConfigParser),注意變數首字母大寫與普通變數區別;

2)        然後建立一個匿名函式並執行( (function () {/*xxxx*/ })(); ),在匿名函式內部建立區域性變數和函式,它們只能在當前作用域中被訪問到;

3)        全域性變數(var PetConfigParser)可以在任何地方訪問到,在匿名函式內部操作PetConfigParser新增靜態函式。

使用例項

例項類

JavaScript實現經典的類,總結有三種方法:

●  建構函式方式;

●  原型方式;

●  建構函式+原型的混合方式

建構函式方式

建構函式用來初始化例項物件的屬性和值。任何JavaScript函式都可以用作建構函式,建構函式必須使用new運算子作為字首來建立新的例項。

建構函式方式跟傳統的面嚮物件語言是不是很相識!只不過是class關鍵字用function替換了。

注意:不要省略new否則Person(“tylerzhu”) //==>undefined。當使用new關鍵字來呼叫建構函式時,執行上下文(context)從全域性物件(window)變成一個空的上下文,這個上下文代表了新生成的例項。因此,this關鍵子指向當前建立的例項。所以省略new時,沒有進行上下文切換會在全域性物件中查詢name,沒有找到而建立一個全域性變數name返回undefined。

原型方式

建構函式方式簡單,但是存在一個浪費記憶體的問題。如上面的例子中例項化了兩個物件tyler、saylor,表面上好像沒什麼問題,但是實際上對於每一個例項物件,sayName()方法都是一模一樣的內容,每一次生成一個例項,都必須為重複的內容申請內容。

alert(tyler. sayName == saylor. sayName) 輸出false!!!

Javascript中每一個建構函式都有一個prototype屬性,指向另一個物件。這個物件的所有屬性和方法,都會被建構函式的例項共享

這時tyler、saylor例項的sayName方法,都是同一個記憶體地址(指向prototype物件),因此原型方法更節省記憶體。

但是看tyler.sayName();saylor.sayName();兩者輸出,會看出問題 —— 它們都輸出“saylorzhu”。因為原型所有屬性都共享,只要一個例項改變其他的都會跟著改變,所以例項化物件saylor覆蓋了tyler。

建構函式+原型的混合方式

建構函式方式可以為同一個類的每一個物件分配不同的記憶體,這很適合寫類的時候設定屬性;但是設定方法的時候我們就需要讓同一個類的不同物件共享同一個記憶體了,寫方法用原型的方式最好。所以寫類的時候需要把構造方法和原型兩種方式混合著用(很多類庫提供的建立類的方法或框架的寫類方式本質上都是:建構函式+原型)。

這樣即可通過建構函式構造不同name的人,物件例項也都共享sayName方法,不會造成記憶體浪費。

JavaScript壓縮/合併

JavaScript程式碼壓縮混淆的意義:簡單的說就是為了減小js檔案大小,去掉多餘的註釋和換行縮排等,使得下載起來更快,提高使用者體驗

JavaScript壓縮工具有很多,我推薦使用jQuery現在使用的工具UglifyJS(jQuery以前也使用過多種壓縮工具,如Packer),因為它壓縮效能很好。

“jQuery 1.5 釋出的時候 john resig 大神說所用的程式碼優化程式從Google Closure切換到UglifyJS,新工具的壓縮效果非常令人滿意”

下面是官方效能對比:We’re still a lot better than YUI in terms of compression, though slightly slower. We’re still a lot faster than Closure, and compression after gzip is comparable.

File

UglifyJS

UglifyJS

+gzip

Closure

Closure

+gzip

YUI

YUI

+gzip

jquery-1.6.2.js 91001(0:01.59) 31896 90678(0:07.40) 31979 101527(0:01.82) 34646
paper.js 142023(0:01.65) 43334 134301(0:07.42) 42495 173383(0:01.58) 48785
prototype.js 88544(0:01.09) 26680 86955(0:06.97) 26326 92130(0:00.79) 28624
thelib-full.js 251939(0:02.55) 72535 249911(0:09.05) 72696 258869(0:01.94) 76584

 

Uglifyjs安裝

UglifyJS是基於 NodeJS 的Javascript語法解析/壓縮/格式化工具,所以我們要安裝NodeJS。

N ode.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

JavaScript最早是執行在瀏覽器中,然而瀏覽器只是提供了一個上下文,它定義了使用JavaScript可以做什麼,但並沒有“說”太多關於JavaScript語言本身可以做什麼。事實上,JavaScript是一門“完整”的語言: 它可以使用在不同的上下文中,其能力與其他同類語言相比有過之而無不及。Node.js事實上就是另外一種上下文,它允許在後端(脫離瀏覽器環境)執行JavaScript程式碼。

要實現在後臺執行JavaScript程式碼,程式碼需要先被解釋然後正確的執行。Node.js的原理正是如此,它使用了GoogleV8虛擬機器(GoogleChrome瀏覽器使用的JavaScript執行環境),來解釋和執行JavaScript程式碼。

除此之外,伴隨著Node.js的還有許多有用的模組,它們可以簡化很多重複的勞作,比如向終端輸出字串。因此,Node.js事實上既是一個執行時環境,同時又是一個庫。

●   Windows下面直接下載exe檔案執行即可。(http://nodejs.org/

●   設定代理(公司網路不設定代理無法下載,外網環境不需要)

“npm,全稱是”node packagemanager”,它是node包管理器,第三方的package全是通過npm去安裝的。”

●   為npm設在代理

●   為npm預設選擇http方式,不選用https

●   npm安裝uglify-js

●   驗證安裝是否成功

UglifyJS使用

uglifyjs [ 選項… ] [ 檔案 ]

檔案引數應該放在選項後面,uglifyjs 會讀取檔案中的javascript程式碼進行處理。如果你不指定輸出的檔名,那麼他會把處理後的內容輸出到命令列中。

支援的選項 :

●   -b 或 –beautify – 輸出格式化程式碼,當傳入該引數,下面的附加選項用於更美觀的控制格式化:

●   -i N 或 –indent N – 縮排級別(空格數量)

●   -q 或 –quote-keys – 是否用引號引起字串物件的鍵(預設只會引起不能被正確標誌的鍵名)

●   –ascii -預設 UglifyJS 不處理字元編碼而直接輸出 Unicode 字元,通過傳入該引數將非ASCII編碼的字元轉化為\cXXXX的序列(輸出總按照UTF8編碼,但傳入該選項能得到ASCII編碼的輸出)。

●   -nm 或 –no-mangle – 不改變變數名稱

●   -ns 或 –no-squeeze – 不呼叫 ast_squeeze() 函式(該函式會做多種優化使得結果更小,可讀性略有降低)

●   -mt 或 –mangle-toplevel – 在頂級作用域打亂變數名稱(預設不開啟)

●   –no-seqs – 當呼叫 ast_squeeze() 將會合並多個語句塊為一個語句塊,如 “a=10; b=20; foo()” 將被轉換為 “a=10,b=20,foo()”

●   –no-dead-code – 預設 UglifyJS 將會刪除不被用到的程式碼,傳入該引數禁用此功能。

●   -nc 或 –no-copyright – 預設 uglifyjs 會在輸出後的程式碼中新增版權資訊等註釋程式碼,傳入該引數禁用此功能。

●   -o 檔名 或 –output 檔名 – 指定輸出檔名,如果不指定,則列印到標準輸出(STDOUT)

●   –overwrite – 如果傳入的JS程式碼來自檔案而不是標準輸入,傳入該引數,輸出會覆蓋該檔案。

●   –ast – 傳入該引數會得到抽象的語法樹而不是Javascript,對除錯或瞭解內部程式碼很有用。

●   -v 或 –verbose – 在標準錯誤輸出一些資訊(目前的版本僅輸出操作用時)

●   –extra – 開啟附加優化,這些優化並未得到全面的測試。

●   –unsafe – 開啟其他附加優化,這些優化已知在特定情況下並不安全,目前僅支援:

●   foo.toString() ==> foo+””

●   –max-line-len (預設32K位元組) – 在32K位元組出增加換行符,傳入0禁用此功能。

●   –reserved-names – 一些類庫會依賴一些變數,該引數指定的名稱不會被混淆掉,多個用逗號隔開

下面是我們使用uglifyjs壓縮,PetConfigParser.js的例子:

PetConfigParser.js壓縮前後對比

JavaScript 專案優化總結

JavaScript檔案合併

規則1——減少HTTP請求(Minimize HTTP Requests

Yahoo前端優化效能規則[5]

只有10%~20%的終端使用者響應時間花在接收請求的HTML文件上,剩下的80%~90%時間都花在HTML文件所引用的所有元件(圖片、指令碼、樣式表、Flash等)進行的HTTP請求上。因此,改善響應時間最簡單的辦法就是減少元件數量並由此減少HTTP請求數。

對公共庫合併壓縮在減少size的同時,減少http請求優化網路耗時提升效能。

文件生成

YUIDoc 是一個基於 Node.js 的應用程式,用來根據 JavaScript 的註釋中生成 API 文件,類似 JavaDoc、ASDoc,這也是當前 YUI 用來生成文件的工具。

YUIDoc安裝與使用

l  YUIDoc安裝

與UglifyJS一樣,YUIDoc也是基於Nodejs的一個應用程式,使用npm安裝即可。

校驗安裝是否成功

●   生成文件(一次性生成)

一次性生成該目錄及其子目錄下所有JS的文件 預設在不配置的情況下會生成在當前目錄的out目錄中。

-o, –out <directory path> Path to put the generated files (defaults to ./out)

●   生成文件(實時生成)

YUIDoc還提供了一種實時文件生成的方式,有利於團隊協作開發 比如在SVN上部署YUIDoc實時文件,遞交到SVN的程式碼都會及時生成文件提供團隊使用查閱

預設開放監聽當前目錄檔案變動,開放3000埠 可以通過

http://127.0.0.1:3000/

來訪問文件 如果3000埠被佔用,也可以指定特定埠號

來通過開放5000埠提供文件訪問

YUIDoc標籤

要使用YUIDoc,那麼所有註釋都得安裝YUIDoc的標準來,否則不能正確解析出文件。YUIDoc使用的標籤和其它語言類同,比較容易理解。下面不詳細說明每個標籤,只列舉幾個例子,具體可參加官方文件。例如:

對PetConfigParser類進行註釋:

對類中的變數進行註釋:

對類中函式進行註釋:

預設生成的文件樣式如下:

JavaScript 專案優化總結

參考連結、進一步閱讀

[1]NodeJS,http://nodejs.org/

[2]UglifyJS,https://github.com/mishoo/UglifyJS/

[3]用UglifyJS解析/壓縮/格式化你的Javascript,http://goo.gl/bwf8U

[4]Yahoo前端優化效能規則,http://goo.gl/nfEBg

[5]用YUIDoc文件化JavaScript程式碼,>

[6]YUIDoc官方,http://yui.github.com/yuidoc/

 

相關文章