一.前言
軟件行業(yè)極其缺乏前端人才這是圈內(nèi)的共識了,某種程度上講,同等水平前端的工資都要比后端高上不少,而圈內(nèi)的另一項共識則是——網(wǎng)頁是公司的臉面!
幾年前,谷歌的一項統(tǒng)計表明,如果亞馬遜頁面加載每慢 100ms,將影響他們 1% 的收入;如果谷歌頁面加載慢 500ms,流量將銳減 20%,這個數(shù)據(jù)現(xiàn)在必將更加恐怖!
在前端高性能優(yōu)化(一)、(二)中,筆者介紹了一些關(guān)于前端優(yōu)化的技術(shù),這些技術(shù)都依賴于前人的辛苦努力,但我們?nèi)砸靼椎氖?,前端的情況十分復雜,優(yōu)化前端性能是必須因地制宜、因時制宜。
在本篇文章中,主要介紹的就是在一些條件下,傳統(tǒng)優(yōu)化 JavaScript 的技術(shù)并不像我們認為的那樣適用。
二.JavaScript 模塊化誤區(qū)
加快 JavaScript 加載和執(zhí)行的速度,一直是前端優(yōu)化的一個熱點。因此我們先來說下 JavaScript 模塊化技術(shù)的相關(guān)知識,希望通過實踐來體現(xiàn)模塊化技術(shù)在使用時的注意事項,避免濫用。
為什么會有模塊化技術(shù)?
長久以來,編寫 JavaScript 一直以文件為單位,一般一個類型的 JavaScript 功能代碼會被放在同一個文件里。在一個頁面里,引用的文件一般是寫死的,也就是不管頁面用不用,只要你引入了這個文件,這個文件就會被加載。
舉個例子,我們開發(fā)了一個內(nèi)容復雜、功能強大的頁面,JavaScript 文件大到 500K,當頁面費勁的把這 500K 加載下來,然而用戶真正只使用了這 500K 里極少的一部分功能,但我們又不得不把這 500K 加載下來,因為不同的用戶使用的功能點可能不一樣,我們必須滿足所有需求。
而模塊化技術(shù)提出按需加載,也就是當用戶觸發(fā)該功能的時候,那個功能才真正的被加載。好比 500K 被拆成了 50 個模塊,每個模塊 10K,當用戶觸發(fā)一個功能時,加載 10K,再觸發(fā)再加載,以這樣懶加載的方式來加載模塊,可以很大的提高響應(yīng)速度。這樣,管理模塊懶加載的技術(shù)也隨之誕生。
模塊化技術(shù)并非到處靠譜!!
之前筆者在網(wǎng)上搜索到了一個模塊化技術(shù):SeaJS。它是一個遵循 CommonJS 規(guī)范的 JavaScript 模塊加載框架,可以實現(xiàn) JavaScript 的模塊化開發(fā)及加載機制。與 JQuery 等 JavaScript 框架不同,SeaJS 不會擴展封裝語言特性,而只是實現(xiàn) JavaScript 的模塊化及按模塊加載。
SeaJS 的主要目的是令 JavaScript 開發(fā)模塊化并可以輕松愉悅進行加載,將前端工程師從繁重的 JavaScript 文件及對象依賴處理中解放出來,可以專注于代碼本身的邏輯。說白了就是有 Lazy Load 的特性,用到某模塊時,SeaJS 才會去加載模塊的 JS 文件。我們可以按功能劃分多個模塊,觸發(fā)模塊功能時,SeaJS 先加載功能模塊的文件,然后執(zhí)行相應(yīng)的功能。
這個 SeaJS 擁有的特性,初看非常吸引人,它可以說是新定義了一種開發(fā)和管理 JavaScript 文件的模式。遵循這個模式,你會享受起 JavaScript 的開發(fā)。
實踐證明,它也的確可以使 JavaScript 模塊化,根據(jù)功能劃分模塊,每個模塊對應(yīng)一個 JavaScript 文件,當執(zhí)行到模塊的功能,或者你需要加載模塊時,模塊才會被下載,同時不會造成重復下載。這一切看起來如此的合理,如此的順暢。。。。。
但是在使用后發(fā)現(xiàn)了一些問題:由于當時開發(fā)的網(wǎng)站功能相對簡單,JavaScript 文件并不是非常大,過多的模塊,反而會導致總加載的時間變多了。
由于是 Lazy Load 特性,不適合的模塊劃分導致網(wǎng)站出現(xiàn)反應(yīng)慢的現(xiàn)象,原因是得先加載模塊的文件,才能執(zhí)行模塊的功能。當網(wǎng)絡(luò)情況不好時,該現(xiàn)象表現(xiàn)的更為嚴重!??!
可以說,問題出在了對新技術(shù)的不了解上,從而出現(xiàn)了問題,預(yù)期是 SeaJS 可以處理 JavaScript 優(yōu)化的問題,因為它具有避免加載不必要模塊的功能,結(jié)果反而南轅北轍。
總結(jié)
雖然嘗試出現(xiàn)了問題,但是,收獲也是巨大的,簡單來說就是以下兩點:
1)因地制宜,根據(jù)實際需要使用模塊化技術(shù),切勿跟風技術(shù),在自己沒有完全掌握時,謹慎用于產(chǎn)品中。
2)模塊如果是懶加載的,粒度不能太小,也就是模塊不能太小。
根據(jù)大功能來劃分模塊也是一個不錯的嘗試。
筆者嘗試將所有模塊劃分為基礎(chǔ)模塊和功能模塊,基礎(chǔ)模塊包括頁面頭部,尾部相關(guān)的公共部分,功能模塊則根據(jù)前后臺劃分,前臺部分,又根據(jù)具體的頁面來劃分,能合并到一起的,就合并成一個模塊,增加粒度。結(jié)果 JavaScript 文件減少,也達到了預(yù)期的效果。
從實際體驗來看,對于流量不大的網(wǎng)站來說,沒有必要使用懶加載 JavaScript 的相關(guān)技術(shù),因為本身 JavaScript 文件就不是非常大。因此在網(wǎng)頁加載時,就可以先加載所需要的模塊。避免不必要的延遲。
三.JavaScript 的位置問題
這一部分,我們來說說JavaScript 的位置問題對網(wǎng)頁網(wǎng)站性能的影響。
為什么要考慮位置問題?
其實不管是 CSS 還是 JavaScript,都需要考慮位置的問題,因為 HTML 的渲染和加載順序是從上往下,也就是如果前面插入了 JavaScript 的引用,那么必須等到這個 JavaScript 下載完畢才會渲染后續(xù)的部分。
因此 JavaScript 的插入位置就成為一個值得考慮的問題,因為不適合的位置可能引起渲染的延遲等,造成不好的用戶體驗。
傳統(tǒng)方案帶來的問題和思考
CSS 放在頭部,JavaScript 放在尾部,這是傳統(tǒng)的經(jīng)驗,它的好處是可以讓頁面優(yōu)先渲染, 從而頁面可以快速顯示。
但有事實往往沒有我們預(yù)想的那么美好。
有的時候會出現(xiàn)這么一種情況:當頁面已經(jīng)渲染完畢時,我們立刻去使用網(wǎng)站的功能,但很多時候功能按鈕會沒有反應(yīng)。原因也很簡單,就是 JavaScript 放在頁面的尾部,還沒來得及加載。。。。
這就糾結(jié)了。。。。
兩種 JavaScript 放置方式,一種放在頭部,一種放在尾部(暫時忽略部分放在頭部,部分放在尾部的方式),一個犧牲了渲染速度,一個犧牲了用戶體驗。所以很多時候 js 的問題我們需要做權(quán)衡。
對一般的小型網(wǎng)站來說,用戶體驗問題要遠遠大于頁面渲染的問題,就比如上文提到的那種功能按鈕不可用的情況。而且,如果 JavaScript 不是很大的話,放在頭部就很好,既不會有太久的頁面空白,也能讓其優(yōu)先加載,二者得到了很好的平衡。
因此,很多經(jīng)驗上的東西并不是絕對的,一定要根據(jù)實際的情況,包括功能特點、服務(wù)器網(wǎng)絡(luò)情況等來綜合考慮。
為此,筆者寫下一個自認為較為合理的位置選擇方案,僅供參考。
圖 1. 判斷 JavaScript 放置位置決策表

從上面的分類介紹,我們也可以看出,將功能代碼按類型歸類到不同的 JavaScript 文件是多么的重要,比如應(yīng)該放頭部和應(yīng)該放尾部的代碼,最好不要合并在一起,不要等到出問題要優(yōu)化的時候再去整理和重構(gòu),這樣會增加很多不必要的工作量。
這不僅僅是為自己工作負責,也是為后面要讀你代碼的新人負責。養(yǎng)成好的設(shè)計編碼習慣,也是技術(shù)積累的一部分。最后再根據(jù) JavaScript 文件的功能類型,來決定是放在頁面的頭部還是尾部。
四.怎樣確定是不是 JavaScript 的問題?
這個問題在之前的前端高性能優(yōu)化(一)、(二)中也都簡單的說過,之所以在這再次提及是因為確實覺得這個工具用著很舒服。
沒錯,就是舒服。
隨著信息爆炸時代的到來,網(wǎng)站本身性能也深刻影響著公司的形象、利益等問題。但是大多數(shù)前端測試工具都太碎片化,沒有辦法針對多個使用場景,而且很多都是像 yslow 這樣簡單打個分,也不是真實的用戶體驗。前一段時間在網(wǎng)上找到了一款前端性能優(yōu)化分析工具——Browser Insight,里面的功能相當全面,而且可以針對多個使用場景,包括:PC端,移動微信,移動瀏覽器,移動webview,還是真實的用戶體驗,也就是說,用戶訪問你的網(wǎng)頁是什么樣的,從這個工具中體現(xiàn)出的就是什么樣子的。
基于 JavaScript 這個維度 Bi 做的也是相當豐富了。
首先是腳本錯誤板塊。Bi 里面可以從不同的時間維度查看被監(jiān)控頁面出現(xiàn)過的腳本錯誤,具體信息包括:發(fā)生時間、設(shè)備類型、報錯的瀏覽器及其版本號、錯誤堆棧信息都可以看到,不論是線上還是線下測試或者頁面維護都是夠用了。
不但能看到時間、系統(tǒng)、瀏覽器等,還可以具體定位到出錯的代碼行,這個確實很方便。
圖 2.Bi 腳本錯誤

其次是頁面響應(yīng)時間板塊,這個算是意外的收獲了。通過響應(yīng)時間板塊里面的慢加載追蹤,可以看到本次慢加載的頁面資源加載情況,然后我們就知道該優(yōu)化哪個頁面的哪些 js 、css、img等。
圖 3.Bi 資源列表-時序圖

五.前端優(yōu)化?用戶體驗?
各位看客,如果你真能堅持看到這里,是否會覺得這篇更像解決用戶體驗問題的文章?但是講心里話,前端優(yōu)化的最終目的,難道不是為了改善用戶體驗嗎?
任何基于用戶體驗的方案,都可以叫前端優(yōu)化技術(shù)。
不管使用多么好的機器、多么高的技術(shù),響應(yīng)速度問題總是避免不了的,因此糾結(jié)于什么技術(shù)來解決是不實際的。但是有一款好的工具,使用一些小技巧,也可以改善用戶等待的體驗。
比如,我們?yōu)槊總€非 _target 的超鏈接或者表單的點擊,添加點擊事件處理,處理邏輯也很簡單,就是設(shè)置 3 秒超時,顯示等待對話框。如果在 3 秒內(nèi)頁面發(fā)生跳轉(zhuǎn),那么這個等待對話框就不會出現(xiàn),否則會彈出提示用戶等待,從而及時的將響應(yīng)反饋給用戶,減少用戶空白等待時間。