好用到掉渣的IE8 Developer Tools--JS效能調校經驗
對我來說,IE8最讓人興奮的新功能非"IE8 Developer Tools"莫屬!! (沒騙你們,我有文章為憑: 1 2 3)
上市後,IE8立即取代Firefox成為我開發網頁時的主力測試工具,也開始體驗它的強大威力。
像是可任意下指令的Console視窗我就超愛,把整個網頁玩弄於股掌之間的感覺真好~~
就拿新聞網站為例,先用上次提過的技巧載入jquery-1.3.2.js,用HTML Tab的工具觀察DOM結構,然後可以在Script Tab的Conosle區一行一行下指令,便可邊試邊改,恣意地上下其手(邪笑),享受惡搞的樂趣,哇哈哈哈~~~ (謎之聲: 應該沒人像你這麼無聊吧?)
CSS編輯器可以任意新增Rule、啟用停用Rule的功能也很讚,這點先前已介紹過。
如果你跟IE8 Developer Toolbar還不熟,點部落上好幾篇精彩的介紹文可以為你們搭起友誼的橋樑:
IE8 Dev Tools的介紹已經很多了,今天則來談談我這兩天利用它調校Javascript效能的實戰經驗。
故事是這樣的,手上的案子需要寫一個類似Excel試算表的網頁介面,總共有近1500格(120列*12欄)的大表格,每列有兩個欄位可以輸入數字,而任一格修改數字(如紅框所示)後,會影響整個分配結構,全表有大半的欄位都要重算過(如綠框所示)。
開發初期,我用最直覺的寫法,$("..input..").change()時利用$(this).parent().next()之類的技巧去找前後格子讀取數字,重新計算加總,再將結果回寫其中。一開始只有十來列時速度還OK,等到測試極端案例(120列)時,問題就大了。每更改一格的內容平均要等上3-5秒才會完成更新,有時甚至IE8會跳出Slow Script警告,詢問是否要停止執行。
觀察到這種現象,心中已有不祥預感。顫抖著雙手,用未來使用者的標準配備---老爺PC+IE6組合而成的魔王機(由來)做一次測試,果不其然,IE6以從頭到尾CPU 100%的姿態當住不動,連正常關閉程式都沒辦法。
好了,是該調校效能的時候了。永遠記得效能調校的第一步驟-->要能找出效能衡量的明確指標!
IE8 Dev Tools的Profiler可以分析並記錄各個Javascript函數被呼叫的次數以及執行所耗時間,無疑是絕佳的觀察工具。操作的方法是,先按下"Start Profiling"(橘框所在),接著在網頁上進行操作,再按下"Stop Profiling"。每次按下"Stop Profiling",就會產生一份Report,介面上有個下拉選單(藍框處)可以切換多次測試的結果進行比較。
報告結果可以依函數做統計,但我偏好的是"Call Tree"方式(紫框),可像Call Stack一樣,列出函數間彼此呼叫的來龍去脈。一般來說,我們首先關心的是累計執行時間,值愈大代表效能愈差。因此,整個調校的過程就是設法讓這個數字愈來愈小!
Call Tree檢視的好處在於,你可以先鎖定最慢的函數,展開它所呼叫的函數,再找出這些子呼叫中最慢的再展開,一路找下去,很快就能查到效能不彰的根源並加以改善,立竿見影。找到有問題的函數,右方會有函數所在的js檔案URL以及列數,點兩下,IE8 Dev Tools馬上就會在Script Tab開啟該js,並捲到該列程式所在位置,貼心到破錶吧! 當然,要改JS程式內容得回VS2008,修改後重新整理網頁,再做一次同樣動作並用Profiler監測,比較看看修改後累積耗用時間是否明顯下降。
重覆以上的步驟,我終於每次改值重算的時間由4,5秒降到1秒左右,雖不滿意,但可接受了。
應該有人很好奇Javascript程式要怎麼修改才會變快,我想這部份Case by Case,並無通用的簡便法則,有時甚至要違背一般的程式設計準則才能達到目的。(就像設計資料庫Schema時,透過"反正規化"改善查詢效率一樣)
但我還是整理一下本次修改的心得:
- 原本的設計將數值直接存在td裡,讀寫時都要涉及UI元素的存取,是效能不彰的最大元凶。我後來利用Javascript Object在背後建立出對應表格的完整資料陣列,每次重算時先在物件陣列裡算好,再將結果寫回前端。這樣做較不直覺,且程式複雜許多,但存取UI元素比操作Javascript元件慢上數倍,為了達到預期的效能,似乎是唯一解。
- 加總最直覺的算法是用for迴圈累加全部的數字,當其中一個值改變時,更快的寫法是將加總值扣掉原值,加上新值,避免重新跑迴圈重算。一樣的,這種設計較不直覺淺顯,但CPU耗用及速度都改善許多。
- children()比find()快
- 少用Regular Expression
- 為每個td取專屬名字, 利用$("#...")找元素
- 直接存取elem.propName取代$(elem).data("propName")
非良好慣例,但真的變快很多
- $(node).text("...")改寫成node.innerHTML = "..."
因指定的文字內容很單純(純數字),所以可用這種不周全的寫法瓜代。直接寫Javascript不用jQuery,速度變快蠻多,但犠牲了相容性。
- 不需要串接且會被大量呼叫的Plugin,就把return this.each拿掉
注意: 上述橘色斜體字部分表示非正規建議做法,純粹因加速而走的偏鋒,大家參考時要特別注意,切忌誤用!! (有個比喻,就像賽車都不裝方向燈跟喇叭一樣,是針對特殊情境才有的設計,可別任意仿效改裝你的愛車)