場景還原
2019/12/28晚19:05左右储耐,突然被拉進(jìn)微信群箱舞。放出一段微信聊天記錄,公司(國內(nèi)某知名的互聯(lián)網(wǎng)金融公司)高管使用app時(shí)發(fā)現(xiàn)公司app中活動(dòng)推廣頁面打開緩慢刹悴,20+s才打開提针,產(chǎn)品,市場部老大被質(zhì)問施敢。此時(shí)問題來到了我們開發(fā)組內(nèi)周荐。
收到問題后狭莱,組長和其他運(yùn)營人員等其他人員,用手機(jī)訪問同一個(gè)活動(dòng)推廣頁概作,都可以1.5s內(nèi)打開頁面腋妙。
問題設(shè)想
先簡單介紹下項(xiàng)目結(jié)構(gòu)和背景。
該項(xiàng)目是一個(gè)配置項(xiàng)目讯榕,姑且稱之為"活動(dòng)配置項(xiàng)目"骤素,該項(xiàng)目分為一個(gè)配置平臺(tái)和一個(gè)H5展示平臺(tái),在配置平臺(tái)可以配置H5頁面的展示圖片愚屁,按鈕定位济竹,輪播圖等等,通過生成一個(gè)結(jié)構(gòu)化的json文件霎槐,上傳到cdn送浊。H5展示平臺(tái)通過ajax獲取cdn的json文件,同時(shí)解析JSon文件丘跌,并渲染出對(duì)應(yīng)的頁面袭景。同時(shí)需要對(duì)接公司三大平臺(tái)app,所有內(nèi)部有很多和app進(jìn)行交互的JS文件闭树,還有各平臺(tái)特有的統(tǒng)計(jì)JS耸棒。由于公司內(nèi)三大平臺(tái)基本相互獨(dú)立,在技術(shù)層面沒有對(duì)native的一些能力做統(tǒng)一报辱,導(dǎo)致一些JS的功能存在冗余与殃,但是又不得不引入。
"活動(dòng)配置項(xiàng)目"配置生成的主要有兩部分內(nèi)容碍现,第一部分:描述的配置JSon文件奈籽,第二部分:項(xiàng)目中用到的圖片。兩者均存放于cdn中鸵赫。
由上場景還原可猜想,頁面業(yè)務(wù)邏輯應(yīng)該不會(huì)有問題躏升。做以下猜想:并從上往下優(yōu)先級(jí)排查
- 靜態(tài)資源在某些特定網(wǎng)絡(luò)下加載緩慢導(dǎo)致辩棒,包括html,JS膨疏,配置的JSon文件一睁,以及圖片資源。
- 檢查前端頁面代碼佃却,看是否是JS獲取緩慢(阻塞)而導(dǎo)致頁面無法進(jìn)入業(yè)務(wù)中者吁。
排查方向
第一點(diǎn)排查:對(duì)公司所用到的所有cdn進(jìn)行測試,未發(fā)現(xiàn)明顯異常饲帅。在cdn回源測試中复凳,發(fā)現(xiàn)個(gè)別cdn站點(diǎn)出現(xiàn)504異常瘤泪,后經(jīng)排查,應(yīng)該不是由于cdn導(dǎo)致緩慢育八。
第二點(diǎn)排查:服務(wù)器使用情況排查对途,發(fā)現(xiàn)當(dāng)前活動(dòng)部署與一臺(tái)公用iis服務(wù)中,當(dāng)前服務(wù)器中部署了多個(gè)服務(wù)髓棋,可能存在某些時(shí)刻服務(wù)資源爭搶導(dǎo)致服務(wù)響應(yīng)時(shí)間變長的情況实檀。
第三點(diǎn)排查:發(fā)現(xiàn)H5展示平臺(tái)對(duì)JS的引入都放在html的head或body的首部進(jìn)行加載,同時(shí)按声,通過chromeDev tool工具膳犹,發(fā)現(xiàn)一共引入了20左右個(gè)JS文件。
問題定位
因?yàn)闊o法重現(xiàn)公司老總的問題签则,但是既然問題出現(xiàn)了须床,還是要想盡辦法解決問題,進(jìn)行優(yōu)化的怀愧。
前端中JS在未開啟async侨颈,defer時(shí),JS的載入執(zhí)行都是依照從上往下執(zhí)行的芯义。所有黨20個(gè)JS同時(shí)放在首部哈垢,只需要一個(gè)JS載入緩慢,則可以導(dǎo)致后續(xù)的業(yè)務(wù)無法執(zhí)行扛拨。這一點(diǎn)是必須要解決的耘分。
同時(shí)另一點(diǎn),當(dāng)有多個(gè)JS同時(shí)放在首部绑警,每個(gè)JS的下載都需要和服務(wù)器建立連接求泰,在Chrome瀏覽器中建立連接的并發(fā)數(shù)在7個(gè)左右,所有后續(xù)的資源都需要等待计盒。同時(shí)建立多個(gè)連接渴频,都需要進(jìn)行TCP的建立,握手北启,SSL的建立卜朗,在網(wǎng)絡(luò)層面是很大開銷。
問題定位一:對(duì)此給出策略是降低連接的建立咕村,即減少JS的網(wǎng)絡(luò)請(qǐng)求
對(duì)于是否需要把所有的JS放在首部场钉,主要取決于業(yè)務(wù)是否需要依賴這些js。因此對(duì)所有js進(jìn)行了梳理懈涛,把業(yè)務(wù)強(qiáng)依賴相關(guān)的js放在首部加載逛万。而對(duì)于業(yè)務(wù)弱相關(guān)的js放在尾部引用。這里弱相關(guān)的有兩種類型批钠,第一種是類似于統(tǒng)計(jì)功能的宇植,即使js載入失敗得封,也不會(huì)導(dǎo)致業(yè)務(wù)頁面無法出現(xiàn)。第二種是只會(huì)在頁面的點(diǎn)擊事件等才會(huì)用到的js当纱∏好浚基于第二種的,還是會(huì)存在js沒有加載成功坡氯,此時(shí)點(diǎn)擊事件中業(yè)務(wù)邏輯無法完成晨横。
問題定位二:理清強(qiáng)依賴js和弱依賴js,分開進(jìn)行首尾分別加載
文件的大小在文件傳輸時(shí)間中占有很大的因素箫柳,所以對(duì)文件進(jìn)行特定的處理手形,會(huì)有比較大影響
問題定位三:對(duì)未進(jìn)行壓縮的js進(jìn)行壓縮處理。同時(shí)在服務(wù)器開啟gizp壓縮悯恍,減少網(wǎng)絡(luò)傳輸?shù)淖止?jié)大小
基于定位的三點(diǎn)問題库糠,得到的整體優(yōu)化方案是:優(yōu)化cdn回源配置,將活動(dòng)遷移到單獨(dú)的服務(wù)器上涮毫。在前端層面理清強(qiáng)依賴的js放在首部瞬欧,弱依賴的js放在尾部,同時(shí)對(duì)首部的js進(jìn)行合并壓縮處理罢防,對(duì)尾部的js進(jìn)行壓縮合并出來艘虎。并且服務(wù)器開啟gzip壓縮
修正問題
基本確定了優(yōu)化方向,剩下就是如何實(shí)現(xiàn)優(yōu)化方案了咒吐。
- 厘清頁面渲染依賴關(guān)系野建,將強(qiáng)制依賴放在首部,非依賴或弱依賴放在尾部恬叹。
- 項(xiàng)目前期并沒有使用任何構(gòu)建工具候生,并沒有對(duì)一些js進(jìn)行壓縮。優(yōu)化的方案是使用gulp對(duì)前置依賴js進(jìn)行合并并壓縮绽昼,對(duì)后置弱依賴或者非依賴的js分開打包唯鸭,在引入。
- 保證頁面渲染為第一要?jiǎng)?wù)硅确,剩下的其他sdk初始化等操作都等最后再進(jìn)行肿孵。
測試性能報(bào)告
修改前
修改前在ChromeDev Tool工具下網(wǎng)絡(luò)的連接傳輸情況。如下疏魏,從圖上可以看到,頁面載入了20個(gè)js晤愧,從這張圖反饋出來的信息除了載入js的多少大莫,在waterfall一欄中,可以看到諸多的黃色和紫色官份,期表示的就是網(wǎng)絡(luò)連接必要的只厘,黃色為建立連接的時(shí)間烙丛,紫色則是SSL建立連接的時(shí)間。而造成這個(gè)問題最主要的原因猜測是由于Chrome建立連接的并發(fā)數(shù)只能在7個(gè)左右羔味,多余的資源需要下載河咽,這需要排隊(duì)等待,并且可能導(dǎo)致連接斷開赋元,無法復(fù)用連接忘蟹,在這里就已經(jīng)花費(fèi)了較多的時(shí)間。
修改后
修改后搁凸,js的資源從20個(gè)變?yōu)?0個(gè)媚值,將前置依賴打包成vendor.js,將app的弱依賴或者是非依賴打包成app.js护糖。通過減少js的請(qǐng)求褥芒,以減低http連接的建立和ssl的建立,減少服務(wù)器的io操作嫡良。
從ChromeDev Tool中的waterfall已經(jīng)不見了黃色和紫色锰扶,證明網(wǎng)絡(luò)方面表現(xiàn)良好。
總結(jié)
凡是免不了要總結(jié)一下
前端的表現(xiàn)性能直接影響著用戶的體驗(yàn)寝受,畢竟是里用戶最近的坷牛,前端工程師更應(yīng)該站用戶的角度去理解產(chǎn)品。話說回來羡蛾,前端優(yōu)化拋開前端代碼層面的實(shí)現(xiàn)因素漓帅,影響最大的莫過于網(wǎng)絡(luò)的因素。而網(wǎng)絡(luò)的因素是復(fù)雜的痴怨,資源的訪問取決于你的網(wǎng)絡(luò)架構(gòu)是怎么樣的忙干。
從最簡單的:客戶端發(fā)起請(qǐng)求 -> (建立連接)-> 資源服務(wù)器返回資源 -> 客戶端執(zhí)行并渲染。
但是浪藻,為了提升服務(wù)的可用性捐迫,網(wǎng)絡(luò)架構(gòu)會(huì)使用代理服務(wù)器
使用了代理服務(wù):
客戶端發(fā)起請(qǐng)求 -> (建立連接) -> 代理服務(wù)器檢查資源是否有效 -> (有效) -> 資源返回 -> 客戶端執(zhí)行并渲染
客戶端發(fā)起請(qǐng)求 -> (建立連接) -> 代理服務(wù)器檢查資源是否有效 -> (無效) -> 訪問資源服務(wù)器 -> 資源返回 -> 代理服務(wù)器緩存(繼續(xù)響應(yīng)客戶端) -> 客戶端執(zhí)行并渲染
又但是,如果某些資源是存放cdn中爱葵,則資源又需要訪問cdn和回源等問題
客戶端發(fā)起請(qǐng)求 -> (尋找最近節(jié)點(diǎn)的cdn服務(wù)器施戴,建立連接) -> cdn是否存在資源 -> (存在) -> 資源返回 -> 客戶端執(zhí)行并渲染
客戶端發(fā)起請(qǐng)求 -> (尋找最近節(jié)點(diǎn)的cdn服務(wù)器,建立連接) -> cdn是否存在資源 -> (不存在) -> 回源處理 -> 資源返回 -> 客戶端執(zhí)行并渲染
當(dāng)一個(gè)頁面的包換的資源存在不一樣的部署方式時(shí)萌丈,在排查問題時(shí)赞哗,就需要厘清最有可能導(dǎo)致問題的關(guān)鍵點(diǎn)。這當(dāng)然取決于發(fā)生情況時(shí)候的具體表現(xiàn)辆雾。在網(wǎng)絡(luò)方面的排查基本上就是回溯資源的訪問過程肪笋。
回到最后,代碼層面上的排查,是否存在影響代碼執(zhí)行藤乙,或者耗時(shí)長的操作猜揪。如前文提到的js載入的問題,其實(shí)在前端已經(jīng)是老生常談了坛梁,在html中而姐,如果未對(duì)js添加defer,async划咐,則js代碼的執(zhí)行依賴于前置js代碼執(zhí)行完成才會(huì)執(zhí)行拴念。這個(gè)則會(huì)阻塞頁面的渲染,所以都將js置于</body>之前尖殃。
對(duì)于復(fù)雜業(yè)務(wù)签舞,將業(yè)務(wù)拆分成多個(gè)模塊處理是一種很好的習(xí)慣蹋辅,這樣有利于以后維護(hù)和需求的遞增官地。但是這樣必然導(dǎo)致資源文件增多疗绣,當(dāng)資源過多時(shí)則可能引起瀏覽器并發(fā)不夠而耗時(shí)。左右在開發(fā)過程中建議模塊化器躏,但是發(fā)布前還是需要將資源合并壓縮處理俐载,這里也不是絕對(duì),主要還是要看資源的數(shù)量登失,建議js資源文件保持在10個(gè)以內(nèi)遏佣。
除此以外,對(duì)數(shù)據(jù)源這一塊揽浙,可以使用緩存状婶,目前瀏覽器對(duì)localStorage支持度已經(jīng)很好,關(guān)鍵數(shù)據(jù)可以進(jìn)行存儲(chǔ)馅巷。在ajax異步成功前展示緩存數(shù)據(jù)膛虫。當(dāng)然對(duì)一些數(shù)據(jù)準(zhǔn)確性要求很高的,通過一些loading明確提示其實(shí)也是不錯(cuò)的選擇钓猬。
結(jié)束 2020-01-01 一眨眼稍刀,已經(jīng)2020了。
老許敞曹,你要老婆不要账月。只要你開金口,今晚就給你送來澳迫【殖荩—— 《牧馬人》