隨著互聯(lián)網(wǎng)的發(fā)展,尤其是在2000年之后瀏覽器技術(shù)漸漸成熟,Web產(chǎn)品也越來(lái)越豐富,這時(shí)我們被瀏覽器窗口內(nèi)的豐富“內(nèi)容”所吸引,關(guān)注HTML/CSS,深入研究Dom、Bom和瀏覽器的渲染機(jī)制等,接觸JavaScript庫(kù),“前端”這個(gè)職業(yè),由此而生。
前端技術(shù)在這10多年中飛速發(fā)展,到了今天,我們可能發(fā)現(xiàn)“內(nèi)容”的美在視覺(jué)上是有上限的,而性能的追求卻是無(wú)窮無(wú)盡的,前端優(yōu)化的重要性慢慢被前端工程師們發(fā)現(xiàn),尤其是在如今火熱的大型電子商務(wù)網(wǎng)站技術(shù)架構(gòu)中,前端優(yōu)化一定是一項(xiàng)必不可少的工作,今天就給大家?guī)?lái)順豐旗下的跨境電商平臺(tái)——豐趣海淘的前端性能優(yōu)化之路。
為什么需要前端性能優(yōu)化
我們都知道在瀏覽器訪問(wèn)一個(gè)網(wǎng)站時(shí),有10%-20%的時(shí)間是花在下載HTML上,其他80%-90%時(shí)間是花在下載頁(yè)面中所有組件上。如果我們把后端時(shí)間縮短一半,整體響應(yīng)時(shí)間可能只會(huì)縮短5%-10%。然而我們花同樣的精力關(guān)注前端的話,我們能將響應(yīng)時(shí)間縮短到一半之多,這樣的提升可想而知。
可能上面的描述比較抽象,我們來(lái)看一些研究的數(shù)據(jù):
· Amaon 慢 0.1s會(huì)導(dǎo)致1%的用戶放棄交易
· Google 慢 0.4s會(huì)導(dǎo)致0.6%的用戶放棄搜索
· Yahoo! 慢 0.4s會(huì)導(dǎo)致減少 5%-9% 的流量
· Bing 慢 2s會(huì)導(dǎo)致收入下降 4.3 %
如果將這些數(shù)據(jù)換算成金錢(qián)上的話,那將是一筆無(wú)法估量的損失,前端優(yōu)化勢(shì)在必行。
怎么樣才算足夠快
對(duì)于豐趣海淘來(lái)說(shuō),考察前端性能的參考指標(biāo)一般有兩個(gè):一、首頁(yè)加載時(shí)間;二、頁(yè)面大小。頁(yè)面大小方面每次新版迭代時(shí)候通過(guò)測(cè)試人員去檢測(cè),加載時(shí)間就需要使用第三方監(jiān)測(cè)工具去監(jiān)測(cè)實(shí)際用戶加載時(shí)間。
這些指標(biāo)具體到實(shí)際使用中分為三個(gè)層次:
· 0.1秒:用戶直接操作ui中對(duì)象的感覺(jué)極限。例如,用戶直接選擇表格的一列到該列高亮顯示,或者反饋被選擇的時(shí)間間隔。
· 1秒:用戶隨意在計(jì)算機(jī)指令空間操作而無(wú)需過(guò)度等等時(shí)間的感覺(jué)極限。0.2-1.0的時(shí)間延遲會(huì)被用戶注意到,會(huì)讓用感覺(jué)到計(jì)算機(jī)正在對(duì)指令進(jìn)行處理中。等待的時(shí)間過(guò)長(zhǎng),會(huì)讓用戶失去流暢的體驗(yàn)。
· 10秒:用戶專注于任務(wù)的極限,超過(guò)10秒的任何操作都要有一個(gè)進(jìn)度指示器,以及有一個(gè)讓用戶中斷操作,而且有清晰的標(biāo)示方法。假設(shè)用戶超過(guò)10秒后返回界面,他們將要重新適應(yīng)。
換句話說(shuō)執(zhí)行如果超過(guò)0.1秒,會(huì)讓人感覺(jué)到不平滑。如果超過(guò)1秒會(huì)讓人感覺(jué)應(yīng)用程序緩慢;超過(guò)10秒那么用戶會(huì)非常沮喪。這些就是用于足夠快的標(biāo)準(zhǔn)。
開(kāi)始優(yōu)化之前
在知道了優(yōu)化的好處和標(biāo)準(zhǔn)以后先來(lái)看看豐趣海淘的架構(gòu)。在制定網(wǎng)站的整體框架時(shí)候,豐趣海淘強(qiáng)調(diào)架構(gòu)的上的前后端分離,這種分離意味著數(shù)據(jù)層、復(fù)雜業(yè)務(wù)邏輯與前端展現(xiàn)和交互的層次分離,這樣做有很多好處:
1、在開(kāi)發(fā)和分工上面可以分得清楚,對(duì)并行開(kāi)發(fā)有很好的效率提升,不被對(duì)方所綁架。在開(kāi)發(fā)時(shí),通過(guò)事先的約定,前端和后端可以同步進(jìn)行。而交接層通過(guò)單元測(cè)試保證交付,可以縮短項(xiàng)目進(jìn)度。
2、結(jié)構(gòu)清晰,前端后端分得較為清楚,所有的后端服務(wù)都會(huì)通過(guò)一個(gè)統(tǒng)一的網(wǎng)關(guān),通過(guò)HTTPS的方式把數(shù)據(jù)通過(guò)接口的方式暴露出來(lái),大部分的頁(yè)面是在前端完成比較輕的邏輯,從這個(gè)角度來(lái)講,不管是前端的WEB還是H5頁(yè)面還是移動(dòng)的客戶端,共享的都是同一套后端服務(wù)的接口。流動(dòng)的數(shù)據(jù),讓前后端可以獨(dú)立的專注的做自己,而不是被對(duì)方所綁架。
豐趣海淘優(yōu)化之路
1、詳情頁(yè)優(yōu)化
豐趣海淘的詳情頁(yè)采用服務(wù)端渲染加客戶端渲染兩種相結(jié)合的方式,服務(wù)端負(fù)責(zé)突出基本信息和圖文說(shuō)明,而客戶端負(fù)責(zé)進(jìn)行熱數(shù)據(jù)的再次請(qǐng)求和熱數(shù)據(jù)的渲染。
優(yōu)化之前,詳情頁(yè)的主文件行數(shù)已經(jīng)達(dá)到2.5k,同時(shí)因?yàn)槊看未蟠僖驗(yàn)橛咳氪罅苛髁浚瑢?dǎo)致詳情頁(yè)的加載非常慢,所以優(yōu)化旨在承擔(dān)代碼重構(gòu)和性能優(yōu)化兩份責(zé)任。
A.代碼重構(gòu)
豐趣的詳情頁(yè)主要有如下幾部分組成:商品信息、推薦商品、評(píng)價(jià)信息、最近瀏覽等。其中商品信息中包含價(jià)格信息和購(gòu)買(mǎi)區(qū)域等等。為了減輕主文件的復(fù)雜度以及配合后面提及的性能優(yōu)化,這里特對(duì)分出四大組件:推薦商品組件、評(píng)價(jià)組件、最近瀏覽組件和活動(dòng)組件,這幾個(gè)文件相互獨(dú)立渲染。在后面的組件懶加載中會(huì)進(jìn)行詳細(xì)說(shuō)明。
詳情頁(yè)中最復(fù)雜的部分就是活動(dòng)。不同活動(dòng)展示的價(jià)格不同,購(gòu)買(mǎi)按鈕也可能不同,當(dāng)前豐趣的活動(dòng)有秒殺、預(yù)售、搭配購(gòu)、限時(shí)促銷等等,另外還有倒計(jì)時(shí)配合各個(gè)活動(dòng)。如果不采用活動(dòng)隔離的方案,那將會(huì)產(chǎn)生大量的if else,造成代碼難以插腳。
針對(duì)這個(gè)問(wèn)題,分離出了秒殺、預(yù)售、搭配購(gòu)、限時(shí)促銷等活動(dòng)類,同時(shí)抽取了一個(gè)活動(dòng)父類供他們繼承。
當(dāng)前已經(jīng)拆分出來(lái)的活動(dòng)類:

價(jià)格、購(gòu)買(mǎi)區(qū)域組件引用繼承關(guān)系如下:

main為入囗,object為活動(dòng)類父類,normal為普通的活動(dòng)。其他活動(dòng)類在類關(guān)系圖中不一一列出
這樣就做到了活動(dòng)類的徹底解耦,后續(xù)增加活動(dòng)只要新建一個(gè)文件即可。
B.性能優(yōu)化
B.1.組件懶加載
組件懶加載參應(yīng)用于首頁(yè)和活動(dòng)頁(yè),因?yàn)檫@些頁(yè)面組件之間的關(guān)聯(lián)性較少,適合組件的懶加載。而詳情頁(yè)的組件之間有的是有關(guān)聯(lián)的,但是豐趣的這些關(guān)聯(lián)當(dāng)前都是可以通過(guò)某種手段規(guī)避的,所以在詳情頁(yè)中也進(jìn)行了組件懶加載的大膽嘗試。
前面提到,豐趣的詳情頁(yè)組要分出了四大組件:推薦商品組件、評(píng)價(jià)組件、最近瀏覽組件和活動(dòng)組件。因?yàn)檫@些組件相對(duì)獨(dú)立,把推薦商品組件、評(píng)價(jià)組件和最近瀏覽組件定位成為懶加載組件,而活動(dòng)組件因?yàn)樵诘谝黄辆鸵M(jìn)行展示,所以沒(méi)有把它定義為懶加載組件。
所以這里就總結(jié)下
1、使用組件懶加載的目的:減少DOM節(jié)點(diǎn)和請(qǐng)求數(shù),以達(dá)到第一時(shí)間渲染出首屏供用戶使用。
2、什么樣的組件審核定義為懶加載組件:非首屏的組件才能定義為懶加載組件。當(dāng)然有的組件和其他組件參少有點(diǎn)關(guān)聯(lián),這個(gè)關(guān)聯(lián)程度如果能夠通過(guò)代碼hack掉,那也是可以定義成懶加載組件的,這就看自己的權(quán)衡了。
現(xiàn)在來(lái)看下豐趣的懶加載組件設(shè)計(jì)。
懶加載組件定義:采用HTML標(biāo)簽的方式,當(dāng)前定義的組件是如下三個(gè),推薦商品/評(píng)價(jià)/最近瀏覽。

懶加載組件支持的字段說(shuō)明:
1、Id組件id:唯一標(biāo)示
2、data-floor(預(yù)留 未使用)組件樓層:當(dāng)滾動(dòng)到1樓時(shí),可以預(yù)先加載2樓的數(shù)據(jù)
3、data-positionid組件位置:組件實(shí)例化出來(lái)后放置的位置,用于移動(dòng)組件位置
4、data-widget組件名稱:當(dāng)前已經(jīng)存在的組件
5、data-params組件參數(shù):用于給組件實(shí)例化使用
6、data-callback組件回調(diào):用于組件實(shí)例化之后的回調(diào)
7、widgetlazyloaded是否已經(jīng)加載:如果組件已經(jīng)加載了,該屬性會(huì)被置為true,標(biāo)示已經(jīng)加載渲染過(guò)了,后面就不會(huì)重復(fù)渲染。
懶加載組件解析:我們定義了一個(gè)管理懶加載組件的類:vendor.widgetLazyLoad,該類能夠支持高優(yōu)先級(jí)和低優(yōu)先級(jí)兩種組件:高優(yōu)先級(jí)的組件是在滾動(dòng)時(shí)候也進(jìn)行加載,低優(yōu)先級(jí)的組件只有等滾動(dòng)結(jié)束了才進(jìn)行加載。
B.2.本地存儲(chǔ)
有些配置類接囗無(wú)需每次讀取接囗。當(dāng)前接囗已經(jīng)支持定義本地存儲(chǔ),只需傳遞"throttleMode":true即可,默認(rèn)存儲(chǔ)五分鐘,為安全起見(jiàn),不支持配置時(shí)間。

接囗要求:必須是讀取配置類的接囗。當(dāng)前已經(jīng)支持的接囗只有兩個(gè):b2cmall.getCmsHeaderConfig和search.getSearchHeaderConfigo。當(dāng)然第一個(gè)接囗和第二個(gè)也不一樣,它是會(huì)根據(jù)不同頁(yè)面吐出來(lái)不同數(shù)據(jù),所以這個(gè)要做到頁(yè)面級(jí)別的存儲(chǔ)。
B.3.合并請(qǐng)求
前端頁(yè)面中,有很大一部分性能是耗在和服務(wù)端的接囗交互中的,特別是在大促期間,服務(wù)立巖因?yàn)榇罅空?qǐng)求的涌入處理速度會(huì)變慢,同事帶寬也會(huì)被占滿導(dǎo)致無(wú)法進(jìn)入。如果此時(shí)前端還是一個(gè)接囗依賴一個(gè)接囗的和服務(wù)端進(jìn)行交互,帶來(lái)的必然是用戶界面的不可用為了減少和服務(wù)端的請(qǐng)求交互,詳情頁(yè)中兩個(gè)負(fù)責(zé)讀取價(jià)格信息的重要接囗:活動(dòng)活動(dòng)信息和獲得價(jià)格信息(product.getActivityInfo,product.getProductHotData)已經(jīng)合并為一個(gè)請(qǐng)求發(fā)送,后面簡(jiǎn)稱activityData。

B.4.延遲加載
在詳情頁(yè),獲得購(gòu)物車(chē)數(shù)量和是否已經(jīng)收藏該商品(getTotalCount和isCollectedGoods)優(yōu)先級(jí)比較低,在渲染價(jià)格后再加載,以保證讓用戶在第一時(shí)間可以購(gòu)買(mǎi)商品。

2、前端速度優(yōu)化
就像上面我們提到的“足夠快”,訪問(wèn)速度是用戶體驗(yàn)的第一基準(zhǔn),圍繞著速度其實(shí)有很多可以做的地方,前端的提速首先需要縮短了資源下載的時(shí)間:
· 豐趣海淘在CDN方面選用的是阿里云CDN,目前所有的靜態(tài)資源都是在阿里云的CDN上, 其中包括JS。無(wú)論APP、HTML5頁(yè)面還是Web頁(yè)面,用戶在加載頁(yè)面的時(shí)候都能從最近的CDN節(jié)點(diǎn)下載靜態(tài)資源,從而起到加速效果。
· 豐趣海淘APP會(huì)用到的所有HTML5資源都會(huì)有緩存,緩存能夠比較好的提升用戶使用APP的體驗(yàn)。
3、前端性能監(jiān)控
無(wú)論是大到整個(gè)系統(tǒng)架構(gòu)設(shè)計(jì),還是小到頁(yè)面大小,加載時(shí)間甚至CDN的使用情況,都需要一個(gè)監(jiān)督前端用戶體驗(yàn)情況的工具,豐趣海淘選擇使用聽(tīng)云Browser來(lái)進(jìn)行日常的監(jiān)控工作,希望建立一個(gè)長(zhǎng)效的機(jī)制反饋用戶的使用情況,好及時(shí)做出應(yīng)對(duì)。
§ CDN監(jiān)控
采用了商業(yè)CDN的服務(wù),但向來(lái)CDN服務(wù)質(zhì)量不太透明,效果好壞無(wú)法判斷,之前偶爾有用戶反饋?lái)?yè)面打開(kāi)緩慢的問(wèn)題,然而自己去做監(jiān)測(cè)費(fèi)力不討好,所以通過(guò)使用聽(tīng)云Browser來(lái)幫助實(shí)時(shí)了解用戶在打開(kāi)頁(yè)面過(guò)程中訪問(wèn)到CDN內(nèi)容的加載時(shí)間。我們通過(guò)對(duì)以往數(shù)據(jù)的對(duì)比,可以評(píng)估CDN在全國(guó)范圍內(nèi)的服務(wù)質(zhì)量,一旦發(fā)現(xiàn)加速效果不佳,可以及時(shí)與廠商溝通進(jìn)行調(diào)優(yōu)。同時(shí),結(jié)合聽(tīng)云Network進(jìn)行主動(dòng)式監(jiān)測(cè),還可以發(fā)現(xiàn)解析失敗、無(wú)法建連等網(wǎng)絡(luò)錯(cuò)誤問(wèn)題,對(duì)CDN服務(wù)做到全方位監(jiān)測(cè),從而有效減少了網(wǎng)民的投訴數(shù)量。
§ 頁(yè)面加載時(shí)間監(jiān)控
作為一個(gè)電子商務(wù)網(wǎng)站,在行業(yè)競(jìng)爭(zhēng)激烈的今天,用戶購(gòu)物體驗(yàn)的好壞直接關(guān)系訂單最終是否能夠成交。而在購(gòu)物過(guò)程中,頁(yè)面的加載時(shí)間是非常重要的一個(gè)指標(biāo)。通過(guò)使用聽(tīng)云Browser,我們可以實(shí)時(shí)看到全國(guó)各地用戶頁(yè)面的加載時(shí)間更新,可以第一時(shí)間發(fā)現(xiàn)頁(yè)面加載緩慢的現(xiàn)象,并能追溯到具體某一次慢頁(yè)面打開(kāi)的情況,這個(gè)對(duì)豐趣海淘來(lái)說(shuō)是很重要的參考數(shù)據(jù)。
4、其他優(yōu)化
在此次優(yōu)化中,我們還做了其它優(yōu)化:
1.SVG文件由之前的直接貼在HTML中變成背景圖
2.HTML的壓縮
優(yōu)化結(jié)果
文件結(jié)構(gòu)與發(fā)送請(qǐng)求數(shù):

頁(yè)面性能:
下面是優(yōu)化前后的數(shù)據(jù)對(duì)比,采用10個(gè)詳情頁(yè)平均性能的方式:

可以看到HTML大小、首屏前端時(shí)間和首屏?xí)r間都有明顯提升。
優(yōu)化之路沒(méi)有盡頭
從結(jié)果上來(lái)看,這一次的優(yōu)化是有很大效果的,但這樣優(yōu)化不是第一次也不是最后一次,未來(lái)豐趣海淘還會(huì)有更多的動(dòng)作:
· 除去現(xiàn)在的靜態(tài)資源和JS,未來(lái)豐趣海淘還考慮將部分HTML頁(yè)面放到CDN上,這樣會(huì)有更快的瀏覽速度
· 考慮建立CDN的切換機(jī)制,已應(yīng)對(duì)更多的用戶訪問(wèn)需求
· 在監(jiān)控方面未來(lái)會(huì)考慮增加線上報(bào)警策略,更好的提醒我們運(yùn)維人員去處理體驗(yàn)上的缺陷
優(yōu)化的道路沒(méi)有終點(diǎn),為了那永遠(yuǎn)的“0.1”,豐趣海淘一直在路上。