一.前言
本文的上一篇 JavaScript 踩坑心得— 為了高速(上) 主要和大家分享的是 JavaScript 使用過(guò)程中的基本原則以及編寫(xiě)過(guò)程中的心得分享拖吼,本文主要和大家聊聊在各個(gè)使用場(chǎng)景下的 JavaScript 使用疟丙,以及在性能優(yōu)化方面的優(yōu)化經(jīng)驗(yàn)等
二.各種場(chǎng)景下的 JavaScript
1.用于 UI 應(yīng)用的 JavaScript
與大多數(shù)服務(wù)器端語(yǔ)言一樣艾凯,用于客戶端應(yīng)用的 JavaScript 框架從來(lái)就不缺少柬采。然而,和用在后端應(yīng)用與服務(wù)中一樣,筆者偏好使用較小的模塊罩息,將這些小模塊組合為框架,從而實(shí)現(xiàn)可擴(kuò)展性个扰,可維護(hù)性以及較高的重用度瓷炮。
并且,由于小模塊的特性递宅,后期拍錯(cuò)或者進(jìn)行性能優(yōu)化的時(shí)候也會(huì)非常方便娘香,現(xiàn)在國(guó)內(nèi)外針對(duì)前端 JavaScript 的性能行為分析工具也比較成熟了,例如 Browser Insight办龄、APPdynamic烘绽、Ruxit,都是不錯(cuò)的選擇
2.構(gòu)建 Web 應(yīng)用的 JavaScript
對(duì)于 Web 應(yīng)用俐填,筆者發(fā)現(xiàn)將 React 用于實(shí)現(xiàn) UI安接,Redux 用于狀態(tài)管理,Joi 用于數(shù)據(jù)驗(yàn)證英融,是建立可擴(kuò)展客戶端應(yīng)用的最有效技術(shù)基礎(chǔ)盏檐。這樣建立起來(lái)的應(yīng)用,往往易于操縱驶悟,測(cè)試以及調(diào)試
筆者非常喜愛(ài)該組合的一點(diǎn)胡野,是他們使得遵循「 JavaScript 的兩大支柱」變得更加簡(jiǎn)單。從 0.14 版本開(kāi)始痕鳍,React 增加了對(duì)純函數(shù)的支持给涕,Redux 也使用了純函數(shù)與可組合的 reducers《罨瘢看來(lái),使用典型面向?qū)ο竽J降拇嗳鯌?yīng)用已經(jīng)一去不復(fù)返了恭应。
該組合的另一大優(yōu)點(diǎn)是可用的工具很多抄邀。React 與 Redux 都有 Chrome 開(kāi)發(fā)者工具插件,使得調(diào)試與操作應(yīng)用變得極為簡(jiǎn)單昼榛。而諸多支持模塊境肾,比如 React Hot Loader 與 Webpack 也使得反饋更加及時(shí)。筆者建議你仔細(xì)了解一下這兩個(gè)模塊胆屿。
3.構(gòu)建移動(dòng)應(yīng)用
顯而易見(jiàn)奥喻,如果是構(gòu)建移動(dòng)平臺(tái)的應(yīng)用,筆者肯定會(huì)推薦 React Native非迹。目前环鲤,React Native 僅支持 iOS 與 Android 平臺(tái),因此憎兽,除非你真的需要 Windows 支持冷离,筆者會(huì)統(tǒng)一推薦 React Native吵冒。
還有一個(gè)值得一說(shuō)的就是,現(xiàn)在無(wú)論安卓還是 ios 的 APP 性能優(yōu)化 也非常重要西剥,但是很多公司只注重后端架構(gòu)痹栖、代碼質(zhì)量等問(wèn)題,卻忽視了前端編寫(xiě)時(shí)可能存在的影響因素瞭空。并且揪阿,盡管 JS 測(cè)試對(duì)于確保應(yīng)用如期運(yùn)行非常重要,但人們卻也常常在測(cè)試中花去太多時(shí)間咆畏,并不是說(shuō)不應(yīng)該編寫(xiě)測(cè)試代碼南捂,只是,要小心過(guò)度測(cè)試與過(guò)度模擬鳖眼,所以上文提到過(guò)的 Browser Insight黑毅、newrelic、APPdynamic 等真心都是優(yōu)化前端不錯(cuò)的選擇钦讳。
三.測(cè)試用 JavaScript
1.僅測(cè)試公共 APIs
通過(guò)不專(zhuān)注于內(nèi)部程序矿瘦,只要不破壞模塊內(nèi)的公共 API,我們就能隨心所欲地改變他們愿卒。這意味著缚去,測(cè)試的變化無(wú)需那么頻繁,而且你可以確保自己接收的數(shù)據(jù)正是應(yīng)該收到的數(shù)據(jù)琼开。
2.建立測(cè)試框架
說(shuō)到實(shí)際應(yīng)用的測(cè)試模塊易结,筆者最近迷上了 tape 與 nock。有了這兩個(gè)模塊柜候,筆者就能覆蓋 99% 的測(cè)試(有時(shí)候搞动,筆者必須自行監(jiān)控一些數(shù)據(jù),并使用 Sinon)渣刷。Browser Insight 這款產(chǎn)品無(wú)論是線上還是線下鹦肿,針對(duì)前端頁(yè)面的 JS 錯(cuò)誤都能準(zhǔn)確的定位到,精確到代碼行辅柴,非常方便箩溃。
3.測(cè)試用戶體驗(yàn)(UX)
如果你在打造 Web 應(yīng)用,可能會(huì)想在盡可能多的設(shè)備與瀏覽環(huán)境下測(cè)試用戶體驗(yàn)碌嘀。為了獲得更為直接的反饋涣旨,筆者采用了 Browser Insight,這個(gè)工具的好處就是基于真實(shí)的用戶體驗(yàn)股冗,多維度的定位分析網(wǎng)站的性能問(wèn)題霹陡,例如腳本錯(cuò)誤、ajax調(diào)用、響應(yīng)時(shí)間分布等板塊穆律,而且惠呼,這個(gè)工具支持 PC 端、移動(dòng)瀏覽器峦耘、移動(dòng)微信頁(yè)面剔蹋、APP 等多個(gè)使用場(chǎng)景,基本上能滿足絕大多數(shù)場(chǎng)景的使用需求辅髓。
四.JavaScript 性能優(yōu)化誤區(qū)
1.JavaScript 模塊化使用誤區(qū)
加快 JavaScript 加載和執(zhí)行的速度泣崩,一直是前端優(yōu)化的一個(gè)熱點(diǎn)。因此我們先來(lái)說(shuō)下 JavaScript 模塊化技術(shù)的相關(guān)知識(shí)洛口,希望通過(guò)實(shí)踐來(lái)體現(xiàn)模塊化技術(shù)在使用時(shí)的注意事項(xiàng)矫付,避免濫用。
為什么會(huì)有模塊化技術(shù)第焰?
長(zhǎng)久以來(lái)买优,編寫(xiě) JavaScript 一直以文件為單位,一般一個(gè)類(lèi)型的 JavaScript 功能代碼會(huì)被放在同一個(gè)文件里挺举。在一個(gè)頁(yè)面里杀赢,引用的文件一般是寫(xiě)死的,也就是不管頁(yè)面用不用湘纵,只要你引入了這個(gè)文件脂崔,這個(gè)文件就會(huì)被加載。
舉個(gè)例子梧喷,我們開(kāi)發(fā)了一個(gè)內(nèi)容復(fù)雜砌左、功能強(qiáng)大的頁(yè)面,JavaScript 文件大到 500K铺敌,當(dāng)頁(yè)面費(fèi)勁的把這 500K 加載下來(lái)汇歹,然而用戶真正只使用了這 500K 里極少的一部分功能,但我們又不得不把這 500K 加載下來(lái)偿凭,因?yàn)椴煌挠脩羰褂玫墓δ茳c(diǎn)可能不一樣产弹,我們必須滿足所有需求。
而模塊化技術(shù)提出 按需加載笔喉,也就是當(dāng)用戶觸發(fā)該功能的時(shí)候,那個(gè)功能才真正的被加載硝皂。好比 500K 被拆成了 50 個(gè)模塊常挚,每個(gè)模塊 10K,當(dāng)用戶觸發(fā)一個(gè)功能時(shí)稽物,加載 10K奄毡,再觸發(fā)再加載,以這樣懶加載的方式來(lái)加載模塊贝或,可以很大的提高響應(yīng)速度吼过。這樣锐秦,管理模塊懶加載的技術(shù)也隨之誕生。
模塊化技術(shù)并非到處靠譜!!
之前筆者在網(wǎng)上搜索到了一個(gè)模塊化技術(shù):SeaJS盗忱。它是一個(gè)遵循 CommonJS 規(guī)范的 JavaScript 模塊加載框架酱床,可以實(shí)現(xiàn) JavaScript 的模塊化開(kāi)發(fā)及加載機(jī)制。與 JQuery 等 JavaScript 框架不同趟佃,SeaJS 不會(huì)擴(kuò)展封裝語(yǔ)言特性扇谣,而只是實(shí)現(xiàn) JavaScript 的模塊化及按模塊加載。
SeaJS 的主要目的是令 JavaScript 開(kāi)發(fā)模塊化并可以輕松愉悅進(jìn)行加載闲昭,將前端工程師從繁重的 JavaScript 文件及對(duì)象依賴(lài)處理中解放出來(lái)罐寨,可以專(zhuān)注于代碼本身的邏輯。說(shuō)白了就是有 Lazy Load 的特性序矩,用到某模塊時(shí)鸯绿,SeaJS 才會(huì)去加載模塊的 JS 文件。我們可以按功能劃分多個(gè)模塊簸淀,觸發(fā)模塊功能時(shí)瓶蝴,SeaJS 先加載功能模塊的文件,然后執(zhí)行相應(yīng)的功能啃擦。
這個(gè) SeaJS 擁有的特性囊蓝,初看非常吸引人,它可以說(shuō)是新定義了一種開(kāi)發(fā)和管理 JavaScript 文件的模式令蛉。遵循這個(gè)模式聚霜,你會(huì)享受起 JavaScript 的開(kāi)發(fā)。
實(shí)踐證明珠叔,它也的確可以使 JavaScript 模塊化蝎宇,根據(jù)功能劃分模塊,每個(gè)模塊對(duì)應(yīng)一個(gè) JavaScript 文件祷安,當(dāng)執(zhí)行到模塊的功能姥芥,或者你需要加載模塊時(shí),模塊才會(huì)被下載汇鞭,同時(shí)不會(huì)造成重復(fù)下載凉唐。這一切看起來(lái)如此的合理,如此的順暢霍骄。台囱。。读整。簿训。
但是在使用后發(fā)現(xiàn)了一些 問(wèn)題:由于當(dāng)時(shí)開(kāi)發(fā)的網(wǎng)站功能相對(duì)簡(jiǎn)單,JavaScript 文件并不是非常大,過(guò)多的模塊强品,反而會(huì)導(dǎo)致總加載的時(shí)間變多了膘侮。
由于是 Lazy Load 特性,不適合的模塊劃分導(dǎo)致網(wǎng)站出現(xiàn)反應(yīng)慢的現(xiàn)象的榛,原因是得先加載模塊的文件琼了,才能執(zhí)行模塊的功能。當(dāng)網(wǎng)絡(luò)情況不好時(shí)困曙,該現(xiàn)象表現(xiàn)的更為嚴(yán)重1砺住!慷丽!
可以說(shuō)蹦哼,問(wèn)題出在了對(duì)新技術(shù)的不了解上,從而出現(xiàn)了問(wèn)題要糊,預(yù)期是 SeaJS 可以處理 JavaScript 優(yōu)化的問(wèn)題纲熏,因?yàn)樗哂斜苊饧虞d不必要模塊的功能,結(jié)果反而南轅北轍锄俄。
根據(jù) 大功能來(lái)劃分模塊 也是一個(gè)不錯(cuò)的嘗試局劲。
筆者嘗試將所有模塊劃分為 基礎(chǔ)模塊 和 功能模塊 ,基礎(chǔ)模塊包括頁(yè)面頭部奶赠,尾部相關(guān)的公共部分鱼填,功能模塊則根據(jù)前后臺(tái)劃分,前臺(tái)部分毅戈,又根據(jù)具體的頁(yè)面來(lái)劃分苹丸,能合并到一起的,就合并成一個(gè)模塊苇经,增加粒度赘理。結(jié)果 JavaScript 文件減少,也達(dá)到了預(yù)期的效果扇单。
從實(shí)際體驗(yàn)來(lái)看商模,對(duì)于流量不大的網(wǎng)站來(lái)說(shuō),沒(méi)有必要使用懶加載 JavaScript 的相關(guān)技術(shù)蜘澜,因?yàn)楸旧?JavaScript 文件就不是非常大施流。因此在網(wǎng)頁(yè)加載時(shí),就可以先加載所需要的模塊鄙信。避免不必要的延遲瞪醋。
2.JavaScript 的位置問(wèn)題
這一部分,我們來(lái)說(shuō)說(shuō) JavaScript 的位置問(wèn)題對(duì)網(wǎng)頁(yè)網(wǎng)站性能的影響扮碧。
為什么要考慮位置問(wèn)題趟章?
其實(shí)不管是 CSS 還是 JavaScript,都需要考慮位置的問(wèn)題慎王,因?yàn)?HTML 的渲染和加載順序是從上往下蚓土,也就是如果前面插入了 JavaScript 的引用,那么必須等到這個(gè) JavaScript 下載完畢才會(huì)渲染后續(xù)的部分赖淤。
因此 JavaScript 的插入位置就成為一個(gè)值得考慮的問(wèn)題蜀漆,因?yàn)椴贿m合的位置可能引起渲染的延遲等,造成不好的用戶體驗(yàn)咱旱。
傳統(tǒng)方案帶來(lái)的問(wèn)題和思考
CSS 放在頭部确丢,JavaScript 放在尾部,這是傳統(tǒng)的經(jīng)驗(yàn)吐限,它的好處是可以讓頁(yè)面優(yōu)先渲染, 從而頁(yè)面可以快速顯示鲜侥。
但有事實(shí)往往沒(méi)有我們預(yù)想的那么美好。
有的時(shí)候會(huì)出現(xiàn)這么一種情況:當(dāng)頁(yè)面已經(jīng)渲染完畢時(shí)诸典,我們立刻去使用網(wǎng)站的功能描函,但很多時(shí)候 功能按鈕會(huì)沒(méi)有反應(yīng)。原因也很簡(jiǎn)單狐粱,就是 JavaScript 放在頁(yè)面的尾部舀寓,還沒(méi)來(lái)得及加載。肌蜻。互墓。。
這就糾結(jié)了蒋搜。篡撵。。齿诞。
兩種 JavaScript 放置方式酸休,一種放在頭部,一種放在尾部(暫時(shí)忽略部分放在頭部祷杈,部分放在尾部的方式)斑司,一個(gè)犧牲了渲染速度,一個(gè)犧牲了用戶體驗(yàn)但汞。所以很多時(shí)候 js 的問(wèn)題我們需要做權(quán)衡宿刮。
對(duì)一般的小型網(wǎng)站來(lái)說(shuō),用戶體驗(yàn)問(wèn)題要遠(yuǎn)遠(yuǎn)大于頁(yè)面渲染的問(wèn)題私蕾,就比如上文提到的那種功能按鈕不可用的情況僵缺。而且,如果 JavaScript 不是很大的話踩叭,放在頭部就很好磕潮,既不會(huì)有太久的頁(yè)面空白翠胰,也能讓其優(yōu)先加載,二者得到了很好的平衡自脯。
因此之景,很多經(jīng)驗(yàn)上的東西并不是絕對(duì)的,一定要根據(jù)實(shí)際的情況膏潮,包括功能特點(diǎn)锻狗、服務(wù)器網(wǎng)絡(luò)情況等來(lái)綜合考慮。
為此焕参,筆者寫(xiě)下一個(gè)自認(rèn)為較為合理的位置選擇方案轻纪,僅供參考。
圖 1. 判斷 JavaScript 放置位置決策表
從上面的分類(lèi)介紹叠纷,我們也可以看出刻帚,將功能代碼按類(lèi)型歸類(lèi)到不同的 JavaScript 文件是多么的重要,比如應(yīng)該放頭部和應(yīng)該放尾部的代碼涩嚣,最好不要合并在一起,不要等到出問(wèn)題要優(yōu)化的時(shí)候再去整理和重構(gòu)我擂,這樣會(huì)增加很多不必要的工作量。
這不僅僅是為自己工作負(fù)責(zé)缓艳,也是為后面要讀你代碼的新人負(fù)責(zé)校摩。養(yǎng)成好的設(shè)計(jì)編碼習(xí)慣,也是技術(shù)積累的一部分阶淘。最后再根據(jù) JavaScript 文件的功能類(lèi)型衙吩,來(lái)決定是放在頁(yè)面的頭部還是尾部。
五.怎樣確定是不是 JavaScript 的問(wèn)題溪窒?
這個(gè)問(wèn)題筆者在之前看過(guò)的的前端高性能優(yōu)化(一)坤塞、(二)中 get 到了新的技能點(diǎn),在這里分享給大家澈蚌。
隨著信息爆炸時(shí)代的到來(lái)摹芙,網(wǎng)站本身性能也深刻影響著公司的形象、利益等問(wèn)題宛瞄。但是大多數(shù)前端測(cè)試工具都太碎片化浮禾,沒(méi)有辦法針對(duì)多個(gè)使用場(chǎng)景,而且很多都是像 yslow 這樣簡(jiǎn)單打個(gè)分份汗,也不是真實(shí)的用戶體驗(yàn)盈电。前一段時(shí)間在網(wǎng)上找到了一款前端性能優(yōu)化分析工具——Browser Insight,里面的功能相當(dāng)全面杯活,而且可以針對(duì)多個(gè)使用場(chǎng)景匆帚,包括:PC端,移動(dòng)微信旁钧,移動(dòng)瀏覽器吸重,移動(dòng)webview互拾,還是 真實(shí)的用戶體驗(yàn),也就是說(shuō)嚎幸,用戶訪問(wèn)你的網(wǎng)頁(yè)是什么樣的摩幔,從這個(gè)工具中體現(xiàn)出的就是什么樣子的。
基于 JavaScript 這個(gè)維度 Bi 做的也是相當(dāng)豐富了鞭铆。
首先是 腳本錯(cuò)誤 板塊。Bi 里面可以從不同的時(shí)間維度查看被監(jiān)控頁(yè)面出現(xiàn)過(guò)的腳本錯(cuò)誤焦影,具體信息包括:發(fā)生時(shí)間车遂、設(shè)備類(lèi)型、報(bào)錯(cuò)的瀏覽器及其版本號(hào)斯辰、錯(cuò)誤堆棧信息都可以看到舶担,不論是 線上還是線下測(cè)試或者頁(yè)面維護(hù) 都是夠用了。
不但能看到時(shí)間彬呻、系統(tǒng)衣陶、瀏覽器等,還可以具體定位到出錯(cuò)的代碼行闸氮,這個(gè)確實(shí)很方便剪况。
圖 2.Bi 腳本錯(cuò)誤
其次是頁(yè)面響應(yīng)時(shí)間板塊,這個(gè)算是意外的收獲了蒲跨。通過(guò)響應(yīng)時(shí)間板塊里面的慢加載追蹤译断,可以看到本次慢加載的頁(yè)面資源加載情況,然后我們就知道該優(yōu)化哪個(gè)頁(yè)面的哪些 js 或悲、css孙咪、img等。
圖 3.Bi 資源列表-時(shí)序圖
六.結(jié)語(yǔ)
JavaScript 具備許多獨(dú)特的優(yōu)勢(shì)巡语,筆者甚至敢說(shuō)翎蹈,JavaScript 很可能是目前最重要的語(yǔ)言,因?yàn)樗茉趲缀跛衅脚_(tái)上運(yùn)行男公,而且可以通過(guò)高度可重用荤堪、可組合的方式實(shí)現(xiàn)。
然而枢赔,不熟悉 JavaScript 性能與問(wèn)題的開(kāi)發(fā)者可能很快就會(huì)發(fā)現(xiàn)逞力,對(duì)其代碼庫(kù)進(jìn)行更改變得越發(fā)困難,而且這些改變可能會(huì)導(dǎo)致出其不意的逆反效果糠爬。
筆者建議寇荧,不要像用其他語(yǔ)言寫(xiě)程序那樣編寫(xiě) JavaScript 程序。盡可能利用 JavaScript 獨(dú)有的性能执隧,創(chuàng)建小而簡(jiǎn)單的模塊揩抡。這有助于你保持冷靜户侥,并愛(ài)上 JavaScript 的強(qiáng)大功能。
Happy JavaScripting!
注:本文翻譯自 Kurtis Kemple 的一篇文章峦嗤,由小編加了一些自己的意見(jiàn)和看法蕊唐。
原文地址:https://labs.mlssoccer.com/javascript-at-scale-achieving-high-velocity-160c7d78af03#.egfwqqz0a
Browser Insight 是一個(gè)基于真實(shí)用戶的 Web 前端性能監(jiān)控平臺(tái),能夠幫大家定位網(wǎng)站性能瓶頸烁设,網(wǎng)站加速效果可視化替梨;支持瀏覽器、微信装黑、App 瀏覽 HTML 和 HTML5 頁(yè)面副瀑。想閱讀更多技術(shù)文章,請(qǐng)?jiān)L問(wèn)OneAPM 官方技術(shù)博客恋谭。
本文轉(zhuǎn)自 OneAPM 官方博客