可以在线看av的网站,洗澡BBWBBWBBWBBW毛,大屁股熟女一区二区三区,久久久亚洲精华液精华液精华液

歡迎來(lái)到海淘科技官網(wǎng) 官方微信 官方微博 平面活動(dòng)官網(wǎng)
微信

網(wǎng)絡(luò)傳播媒介服務(wù)提供商

熱線電話

021-62677988

海淘新聞
首頁(yè) > 新聞列表 > 2016年京東首頁(yè)改版前端回顧

2016年京東首頁(yè)改版前端回顧

發(fā)布時(shí)間: 2017-01-03 16:47

整體架構(gòu)

最初聽(tīng)說(shuō)要做新版京東首頁(yè)的時(shí)候,是懷有一絲惶恐的,畢竟是作為京東的門戶,其重要性和受關(guān)注程度自然不言而喻,一行代碼的失誤可能會(huì)造成不可挽回的后果,而且過(guò)去的首頁(yè)無(wú)論性能,還有體驗(yàn)在業(yè)界都已經(jīng)是做得非常優(yōu)秀了,要再想有些出彩的地方,也是十分困難,所以綜上就是壓力山大。當(dāng)然,花開(kāi)兩朵,咱們單表一枝,本文主要還是相對(duì)這次改版工作中提煉的工作方法和優(yōu)化方式做出一定的總結(jié)。

這次改版,在前端架構(gòu)上大體還是沿用過(guò)去的架構(gòu),使用 jQuery + Seajs 這種古老的開(kāi)發(fā)方式,因?yàn)槭醉?yè)還依賴著許多舊的系統(tǒng)與組件,無(wú)法在短時(shí)間內(nèi)對(duì)基礎(chǔ)架構(gòu)進(jìn)行升級(jí),當(dāng)然并不是說(shuō)舊的就不好,要去盲目追求一些新的東西,而是這種架構(gòu)還是有可以提升的地方。

而整個(gè)項(xiàng)目的架構(gòu)是經(jīng)歷之前業(yè)務(wù)進(jìn)行總結(jié)提煉出來(lái)的

Athena前端工程化工具,是我們團(tuán)隊(duì)自己探索開(kāi)發(fā)的一套基于NodeJs的命令行式前端工程化工具,解決了自動(dòng)化編譯、代碼處理、依賴分析、文件壓縮等前端開(kāi)發(fā)中的常規(guī)問(wèn)題,有效地提升了我們的工作效率,解放生產(chǎn)力,目前已經(jīng)應(yīng)用于我們團(tuán)隊(duì)的多個(gè)業(yè)務(wù)中,首頁(yè)改版也使用 Athena來(lái)進(jìn)行開(kāi)發(fā);

Athena管理平臺(tái),是Athena工具配套的管理后臺(tái),它會(huì)收集本地工具操作中上報(bào)的統(tǒng)計(jì)數(shù)據(jù),包括項(xiàng)目、模塊、頁(yè)面、組件創(chuàng)建的信息,文件、資源依賴關(guān)系的信息等,通過(guò)這些數(shù)據(jù)來(lái)進(jìn)行項(xiàng)目和資源的管理,同時(shí)提供了項(xiàng)目模板,方便使用本地工具創(chuàng)建項(xiàng)目時(shí)選擇,具體可以參考之前的博文我們是如何做好前端工程化和靜態(tài)資源管理;

Athena組件平臺(tái),是基于Athena總結(jié)的一套業(yè)務(wù)組件的平臺(tái),可以很好地管理我們的業(yè)務(wù)組件,方便組件的復(fù)用和傳播;

Athena基礎(chǔ)庫(kù)及組件庫(kù),是業(yè)務(wù)中總結(jié)出的基于jQuery + Seajs的js庫(kù),簡(jiǎn)化業(yè)務(wù)開(kāi)發(fā),提供完整的框架;

Athena模擬接口,可以自由編輯生成指定接口的假數(shù)據(jù),用于開(kāi)發(fā)時(shí)真實(shí)接口的替代,讓開(kāi)發(fā)不再依賴后端接口;

Athena兜底接口服務(wù),可以指定接口生成一份兜底數(shù)據(jù)接口,平臺(tái)會(huì)定時(shí)去抓取指定接口數(shù)據(jù),然后生成兜底數(shù)據(jù)到CDN,從而生成對(duì)應(yīng)的兜底接口,這樣讓正常接口多一份兜底保障;

Athena前端監(jiān)控,通過(guò)在頁(yè)面中進(jìn)行埋點(diǎn)上報(bào)的方式,我們可以在監(jiān)控系統(tǒng)中,實(shí)時(shí)地看到性能相關(guān)數(shù)據(jù)。我們進(jìn)行上報(bào)的不止有頁(yè)面性能、速度相關(guān)的數(shù)據(jù),同時(shí)會(huì)上報(bào)用戶的環(huán)境信息,例如操作系統(tǒng)、瀏覽器、網(wǎng)速等,而且還會(huì)對(duì)頁(yè)面中錯(cuò)誤信息進(jìn)行上報(bào),如模塊的隱藏等,通過(guò)這些數(shù)據(jù),對(duì)我們的業(yè)務(wù)進(jìn)行實(shí)時(shí)地監(jiān)控與分析。

在我們的架構(gòu)中,各種各樣的工具與系統(tǒng)相輔相成,覆蓋到了開(kāi)發(fā)到上線的各個(gè)環(huán)節(jié),自成一套體系。這樣的架構(gòu)不止是針對(duì)首頁(yè)這個(gè)業(yè)務(wù)的,而是在基于對(duì)之前業(yè)務(wù)開(kāi)發(fā)總結(jié)的基礎(chǔ)上進(jìn)行完善、調(diào)整的架構(gòu),適用于我們各個(gè)業(yè)務(wù)。而這次首頁(yè)的改版中,我們對(duì)開(kāi)發(fā)模式、性能優(yōu)化、體驗(yàn)優(yōu)化都進(jìn)行了一些新的探索,讓我們對(duì)于業(yè)務(wù)開(kāi)發(fā)的整體解決方案又有了新的改進(jìn)。

開(kāi)發(fā)模式

Athena

開(kāi)發(fā)效率的提升是我們一直追求的,工欲善其事,必先利其器,我們通過(guò)總結(jié)以往的開(kāi)發(fā)工作,提出了各種手段來(lái)優(yōu)化我們的開(kāi)發(fā)效率,前端工具Athena就是其中的一個(gè)產(chǎn)物,當(dāng)然它又不僅僅是為了提升開(kāi)發(fā)效率而已,它是我們總結(jié)出的一套針對(duì)前端開(kāi)發(fā)的完整解決方案,可以讓我們的整體開(kāi)發(fā)流程更加簡(jiǎn)單明了。

Athena提供了統(tǒng)一的項(xiàng)目架構(gòu),根據(jù)業(yè)務(wù)功能不同,我們將一個(gè)項(xiàng)目(app)拆分成不同的業(yè)務(wù)模塊(module),而每一個(gè)模塊都包含自身的頁(yè)面(page)以及構(gòu)成頁(yè)面所需要的組件(widget)。

項(xiàng)目架構(gòu)

在本地使用Athena創(chuàng)建完整的項(xiàng)目結(jié)構(gòu),隨后我們就可以只關(guān)注代碼邏輯的書(shū)寫(xiě),Athena提供了簡(jiǎn)便的操作命令可以一鍵式地實(shí)時(shí)編譯預(yù)覽我們的頁(yè)面,從而讓我們不必去關(guān)心文件處理、代碼編譯等細(xì)節(jié),開(kāi)發(fā)完后,可以通過(guò)Athena執(zhí)行完整的編譯步驟并同步到我們的服務(wù)器上方便進(jìn)行瀏覽測(cè)試。

使用Athena,新版首頁(yè)開(kāi)發(fā)模式大致如下:

前后端分離

基于Athena工具,我們目前已經(jīng)可以做到完全地本地開(kāi)發(fā)調(diào)試了,但是還并不能做到完全的前后端分離,以過(guò)去首頁(yè)為例,頁(yè)面被拆分成首屏和樓層,首屏采用直出的方式以提升速度,樓層則使用異步加載的方式,拉取服務(wù)器上已經(jīng)渲染好的HTML字符串,如圖

整個(gè)頁(yè)面,包括首屏和樓層,都需要前端寫(xiě)好靜態(tài)HTML,然后給后端開(kāi)發(fā)同學(xué)來(lái)套用,轉(zhuǎn)成后端語(yǔ)言對(duì)應(yīng)的模板,這樣導(dǎo)致前后端耦合較深,HTML更新極不方便,開(kāi)發(fā)成本較高。

為了解決這樣前后端耦合的問(wèn)題,減少溝通成本,這次首頁(yè)改版我們使用了新的開(kāi)發(fā)方式,為了保證首屏速度,首屏依然采用直出的方式,但對(duì)非首屏的樓層進(jìn)行改進(jìn),使用前端模板 + 數(shù)據(jù)開(kāi)發(fā)方式,將DOM字符串的渲染放到前端來(lái)做,后端只提供數(shù)據(jù)接口,以此來(lái)達(dá)到前后端分離的效果,同時(shí)在開(kāi)發(fā)中使用假數(shù)據(jù)平臺(tái)模擬接口,讓前端工作不再依賴后端。

在最開(kāi)始提出這樣前后端分離方案的時(shí)候還是受到了不少的質(zhì)疑,因?yàn)槭褂们岸四0?+ 數(shù)據(jù)開(kāi)發(fā)方式,會(huì)使得每個(gè)樓層都多一個(gè)接口,并且需要依靠JS來(lái)動(dòng)態(tài)渲染,會(huì)影響到樓層加載的性能,但經(jīng)過(guò)我們的測(cè)試證明在現(xiàn)代PC瀏覽器下兩種模式前端渲染和后端渲染并不會(huì)相差太多,并且在模板、數(shù)據(jù)雙重緩存下,這樣的差距更是微乎其微了,更關(guān)鍵的是能讓我們的開(kāi)發(fā)效率有所提升。

當(dāng)然,我們對(duì)于性能的追求總是孜孜不倦,為了讓樓層的加載速度更快,減少請(qǐng)求,我們?cè)诤罄m(xù)將使用在服務(wù)端定時(shí)獲取數(shù)據(jù)編譯前端模板,然后生成靜態(tài)文件推送到CDN的方式來(lái)改進(jìn),和之前的由后端開(kāi)發(fā)同學(xué)套模板生成靜態(tài)文件不同的是,我們將自己搭建這樣的中間層服務(wù),在服務(wù)端編譯前端模板,實(shí)現(xiàn)前后端同構(gòu),而前端可以隨時(shí)切換渲染方式,改成請(qǐng)求渲染好的 HTML 字符串來(lái)進(jìn)行加載,以此來(lái)提升性能。

對(duì)性能優(yōu)化的探索

性能永遠(yuǎn)是前端工程師追求的主題,過(guò)去首頁(yè)在性能優(yōu)化上已經(jīng)做得非常極致了,它已經(jīng)使用了各種手段來(lái)優(yōu)化性能,包括首屏直出、樣式直出來(lái)提升首屏速度,樓層按需加載,減少不必要的請(qǐng)求等等,所以在做新版首頁(yè)的時(shí)候,我們感覺(jué)戰(zhàn)戰(zhàn)兢兢,因?yàn)楦陌娌荒茏岉?yè)面受到影響,而且最好還能比原來(lái)更快,所以,這次改版中我們主要通過(guò)如下手段來(lái)進(jìn)行性能的優(yōu)化。

首屏直出和精簡(jiǎn)

首屏直出是讓首屏速度更快的最佳選擇,此次版本依然選擇了首屏直出的方式,直出的內(nèi)容包括首屏HTML,頁(yè)面樓層骨架,以及樣式和一些必須的腳本,看起來(lái)和之前的方式如出一轍,但此次改版我們還是做了很大的改進(jìn),那就是讓首屏更加精簡(jiǎn)。

過(guò)去是將頁(yè)面引用的所有樣式都直出在頁(yè)面上,沒(méi)有外鏈的CSS,各種優(yōu)化手段都考慮進(jìn)去了,那么新版首頁(yè)就只能在精簡(jiǎn)大小上下功夫了,所以新版首頁(yè)的首屏只直出了首屏必須的樣式,同時(shí)只直出一小部分必須的腳本,而非首屏的樓層樣式拆分到各自樓層中,和樓層的模板放在一起,按需加載。

通過(guò)對(duì)Athena工具的改造,我們實(shí)現(xiàn)樣式、模板的統(tǒng)一抽離這一功能,并且是在項(xiàng)目編譯階段自動(dòng)實(shí)現(xiàn)的,開(kāi)發(fā)者勿需關(guān)心。由于Athena統(tǒng)一的項(xiàng)目結(jié)構(gòu),每一個(gè)樓層在我們的項(xiàng)目中對(duì)應(yīng)一個(gè)widget的組件,組件包含自己的HTML 、CSS、 JavaScript文件,同時(shí)widget的組件是可以繼續(xù)調(diào)用其他widget的組件的,所以在編譯時(shí),工具會(huì)自動(dòng)分析所有widget的依賴關(guān)系,然后把樓層的模板和所有引用到樣式打包到一個(gè)文件中。最后在樓層加載的時(shí)候去請(qǐng)求這個(gè)文件,然后解析加載。這樣的抽離工作會(huì)在最后的項(xiàng)目編譯階段進(jìn)行,而進(jìn)行本地開(kāi)發(fā)預(yù)覽的時(shí)候并不會(huì)執(zhí)行,這樣保證了開(kāi)發(fā)的效率。

// https://misc.360buyimg.com/mtd/pc/index/home/rec_tpl.min.jsjsonCallBack_rec_tpl({ dom: '{%var i,clstagPrefix = pageConfig.clstagPrefix + o.staticLogTag;var isWide = pageConfig.compatible && pageConfig.wideVersion;%}{% var len = o.list.length; len = Math.min(len, 3); %}{% if (len >= 1) { %}
{% for(i = 0; i < len; i++){ %}{% var item = o.list[i]; %}{% var imgUrl = isWide ? item.imgUrl : item.imgUrlB; %}
{%= item.title %}
{% } %}
{% } %} ', style: ".rec_list{overflow:hidden;height:100px}.rec_item{overflow:hidden;float:left;width:396px;height:100%}.rec_lk{display:block;height:100%}.rec_img{display:block;margin:auto}.o2_mini .rec_item{width:330px}.csstransitions .rec_img{-webkit-transition:opacity .2s;-moz-transition:opacity .2s;transition:opacity .2s}.csstransitions .rec_lk:hover .rec_img{opacity:.8}", time: 1479195351434, version: "ff78610a0ef9cdbb"});

通過(guò)上述手段,我們讓首屏變得更加精簡(jiǎn),從下面的對(duì)比中就可以看出

這是過(guò)去首頁(yè)首屏大小


過(guò)去首頁(yè)首屏

這是新版首頁(yè)首屏大小


新版首頁(yè)首屏

可以看出優(yōu)化精簡(jiǎn)之后,新版的首屏的大小減小了非常之多。

首屏輪播第一幀直出

一直以來(lái)輪播都是靠頁(yè)面最后加載的JS來(lái)進(jìn)行渲染的,因?yàn)檩啿D有隨機(jī)渲染圖片的邏輯需要依賴JS,但在一段時(shí)間的觀察之后發(fā)現(xiàn),如果CDN出現(xiàn)抖動(dòng),或者用戶的網(wǎng)速較慢,那么首屏輪播這一塊位置就會(huì)一直空著,給人的體驗(yàn)非常不好。所以在這一版的首頁(yè)中我們將輪播圖第一幀的數(shù)據(jù)直出在頁(yè)面上,同時(shí)也將第一幀的渲染邏輯也直出在頁(yè)面上,這樣一來(lái),首屏輪播出來(lái)得就非??欤瑴p少用戶的等待時(shí)間。

樓層按需加載與滾動(dòng)優(yōu)化

首屏直出后,非首屏的內(nèi)容肯定也不會(huì)一次性全部加載,因?yàn)橄袷醉?yè)這樣的頁(yè)面樓層非常之多,一次性加載全部不僅僅慢,而且對(duì)接口來(lái)說(shuō)也是一種損耗,所以我們考慮將樓層按需加載。

在我們新的方案中,已經(jīng)采用了前端模板+數(shù)據(jù)的開(kāi)發(fā)模式,所以在開(kāi)發(fā)中我們想用直接書(shū)寫(xiě)前端模板的方式來(lái)進(jìn)行開(kāi)發(fā),然后在本地進(jìn)行預(yù)覽,而在項(xiàng)目編譯時(shí)能將我們的模板編譯成獨(dú)立的文件,方便渲染邏輯進(jìn)行加載。所幸Athena工具已經(jīng)支持了這樣的功能,在開(kāi)發(fā)中我們以編寫(xiě)前端模板的方式去開(kāi)發(fā)整個(gè)頁(yè)面,隨后通過(guò)編譯工具,在代碼編譯階段自動(dòng)將樓層的模板和樣式抽離成一個(gè)與組件同名的獨(dú)立JS文件,通過(guò)頁(yè)面加載邏輯去按需拉取模板文件,再進(jìn)行渲染。

下面例子揭示了樓層模板生成的過(guò)程

直接書(shū)寫(xiě)前端模板,編寫(xiě)模板時(shí)我們給模板加上標(biāo)記位 o2-out-tpl

在編譯時(shí)掃描依賴關(guān)系,生成模板JS文件依賴組件的關(guān)系表

"dependency": { "elevator_tpl.js": [], "entry_tpl.js": [ { "widgetName": "spetit", "module": "home", "moduleId": "f1c9d790-765a-11e6-927d-63ab47c8eeb2", "widgetType": "widget", "exists": true } ], "fbt_tpl.js": [ { "widgetName": "find", "module": "home", "moduleId": "f1c9d790-765a-11e6-927d-63ab47c8eeb2", "widgetType": "widget", "exists": true }, { "widgetName": "brand", "module": "home", "moduleId": "f1c9d790-765a-11e6-927d-63ab47c8eeb2", "widgetType": "widget", "exists": true }, { "widgetName": "top", "module": "home", "moduleId": "f1c9d790-765a-11e6-927d-63ab47c8eeb2", "widgetType": "widget", "exists": true } ]}
通過(guò)關(guān)系表去合并處理CSS樣式,再和前端模板一起計(jì)算出MD5,生成獨(dú)立的JS文件
jsonCallBack_rec_tpl({dom:'{%var i,clstagPrefix = pageConfig.clstagPrefix + o.staticLogTag;var isWide = pageConfig.compatible && pageConfig.wideVersion;%}{% var len = o.list.length; len = Math.min(len, 3); %}{% if (len >= 1) { %}
{% for(i = 0; i < len; i++){ %}{% var item = o.list[i]; %}{% var imgUrl = isWide ? item.imgUrl : item.imgUrlB; %}
{%= item.title %}
{% } %}
{% } %} ',style:".rec_list{overflow:hidden;height:100px}.rec_item{overflow:hidden;float:left;width:396px;height:100%}.rec_lk{display:block;height:100%}.rec_img{display:block;margin:auto}.o2_mini .rec_item{width:330px}.csstransitions .rec_img{-webkit-transition:opacity .2s;-moz-transition:opacity .2s;transition:opacity .2s}.csstransitions .rec_lk:hover .rec_img{opacity:.8}",time:1479466862559,version:"ff78610a0ef9cdbb"});

同時(shí)會(huì)在邏輯腳本入口位置自動(dòng)加入模板的版本號(hào)


{ "elevator_tpl": "e4d5dbaa3ecd12d2", "entry_tpl": "e3150fce4b2b332a", "fbt_tpl": "18f8bff18188a453", "floor_coupon_tpl": "1559694cb962e0d6", "floor_ract_tpl": "13b92d16fb6e2f7a", "mod_footer_tpl": "49142394d0e7f24e", "more_tpl": "d300081dd7f13f78", "portal_tpl": "68fae801a032cf93", "rec_tpl": "ff78610a0ef9cdbb", "seckill_tpl": "f11d04fd7eabc0e6"}


模板文件通過(guò)系統(tǒng)發(fā)布到CDN后,我們就需要有一套加載邏輯來(lái)進(jìn)行加載。通過(guò)監(jiān)聽(tīng)滾動(dòng)事件,我們判斷讓處于瀏覽器視窗內(nèi)的樓層進(jìn)行加載,由于監(jiān)聽(tīng)了滾動(dòng)事件,為了讓滾動(dòng)更加流暢,我們必然要對(duì)滾動(dòng)中做的操作進(jìn)行優(yōu)化。為了避免滾動(dòng)操作不斷被觸發(fā),需要對(duì)滾動(dòng)進(jìn)行節(jié)流處理。我們的原則是盡量避免在滾動(dòng)的時(shí)候進(jìn)行DOM操作與復(fù)雜計(jì)算,所以在渲染邏輯初始化的時(shí)候,我們就已經(jīng)收集好了樓層的相關(guān)信息,包括樓層高度、樓層的offsetTop等,這樣在滾動(dòng)的時(shí)候就不再需要進(jìn)行任何DOM操作了,讓滾動(dòng)的效率有所提升。而當(dāng)樓層的數(shù)據(jù)例如樓層高度發(fā)生變化時(shí),則通過(guò)消息通知的機(jī)制來(lái)實(shí)時(shí)地更新樓層信息即可。

腳本延后加載執(zhí)行

除了樓層是按需加載的,頁(yè)面中用到的一些腳本文件也是盡量延后加載、執(zhí)行。Athena工具在代碼打包的時(shí)候,會(huì)對(duì)每個(gè)獨(dú)立的文件進(jìn)行單獨(dú)處理,同時(shí)生成一份靜態(tài)資源的線上對(duì)應(yīng)表,在編譯的最后會(huì)將引用的資源替換成配置的線上絕對(duì)地址。我們可以使用Seajs提供的require.asyncAPI來(lái)進(jìn)行異步加載資源,這樣讓資源加載更加合理。


// 開(kāi)發(fā)中的代碼require.async(__uri('APP_JS_ROOT/header.js'))// 編譯后require.async('//misc.360buyimg.com/mtd/pc/index/js/header.js')



同時(shí),還有業(yè)務(wù)上一些統(tǒng)計(jì)上報(bào)等邏輯,可以放到 window onload 事件之后再執(zhí)行,這樣可以避免由于類似統(tǒng)計(jì)這樣的請(qǐng)求占用到頁(yè)面加載資源,從而降低頁(yè)面 onload 時(shí)間。

模板、數(shù)據(jù)分離緩存

每個(gè)樓層都按需加載之后,每次去加載這個(gè)樓層是否都要重新去請(qǐng)求這個(gè)樓層的模板和數(shù)據(jù)呢?答案當(dāng)然是否定的。

目前大部分瀏覽器已經(jīng)提供了許多前端緩存的解決方案,而其中兼容性最好,易用性最強(qiáng)的非localStorage莫屬。利用localStorage我們可以對(duì)模板和數(shù)據(jù)進(jìn)行緩存,這樣當(dāng)用戶第二次加載的時(shí)候就可以不用再去請(qǐng)求網(wǎng)絡(luò)資源,而可以直接從本地獲取了。

但緩存之后如何進(jìn)行更新呢?我們可以通過(guò)進(jìn)行MD5校驗(yàn)版本來(lái)實(shí)現(xiàn)。

對(duì)數(shù)據(jù)來(lái)說(shuō),數(shù)據(jù)是由后端給出的,我們可以讓后端同學(xué)將可以緩存的接口數(shù)據(jù)計(jì)算出一個(gè)MD5值作為版本號(hào),然后直出在頁(yè)面上,同時(shí)在接口中返回這個(gè)版本號(hào),這樣當(dāng)前端去加載是首先判斷版本號(hào)是否一致,以此來(lái)判斷是直接讀緩存還是從網(wǎng)絡(luò)請(qǐng)求資源。

接口版本號(hào)

而對(duì)于模板來(lái)說(shuō),則可以通過(guò)Athena工具,在每次編譯的時(shí)候自行計(jì)算出版本號(hào),寫(xiě)入模板文件和入口JS文件中,這樣在模板加載的時(shí)候也可以進(jìn)行比對(duì)。

單個(gè)模板文件


// https://misc.360buyimg.com/mtd/pc/index/home/rec_tpl.min.jsjsonCallBack_rec_tpl({ dom: '{%var i,clstagPrefix = pageConfig.clstagPrefix + o.staticLogTag;var isWide = pageConfig.compatible && pageConfig.wideVersion;%}{% var len = o.list.length; len = Math.min(len, 3); %}{% if (len >= 1) { %}
{% for(i = 0; i < len; i++){ %}{% var item = o.list[i]; %}{% var imgUrl = isWide ? item.imgUrl : item.imgUrlB; %}
{%= item.title %}
{% } %}
{% } %} ', style: ".rec_list{overflow:hidden;height:100px}.rec_item{overflow:hidden;float:left;width:396px;height:100%}.rec_lk{display:block;height:100%}.rec_img{display:block;margin:auto}.o2_mini .rec_item{width:330px}.csstransitions .rec_img{-webkit-transition:opacity .2s;-moz-transition:opacity .2s;transition:opacity .2s}.csstransitions .rec_lk:hover .rec_img{opacity:.8}", time: 1479195351434, version: "ff78610a0ef9cdbb"});

JS入口文件

// https://misc.360buyimg.com/mtd/pc/index/home/index_focus.min.jswindow.tplVersion = { "1212_tpl": "ce7dcd7cd0beacb2", elevator_tpl: "e4d5dbaa3ecd12d2", entry_tpl: "2caa7cd543c322ea", fbt_tpl: "18f8bff18188a453", floor_coupon_tpl: "b98cf33be84aae98", floor_ract_tpl: "13b92d16fb6e2f7a", mod_footer_tpl: "072072ffc47778be", more_tpl: "25dcb060800c503a", portal_tpl: "68fae801a032cf93", rec_tpl: "ff78610a0ef9cdbb", seckill_tpl: "4fee56c5b073e5e1"};

通過(guò)上述方式,我們實(shí)現(xiàn)了模板、數(shù)據(jù)的分離緩存,由于樓層類似的關(guān)系,頁(yè)面中的模板大多數(shù)是重復(fù),這樣子模板緩存起來(lái)就能大大提高模板的利用率,當(dāng)用戶第二次訪問(wèn)的時(shí)候?qū)⒉粫?huì)再產(chǎn)生請(qǐng)求,在加速訪問(wèn)的同時(shí),減少網(wǎng)絡(luò)帶寬消耗,并且如果數(shù)據(jù)發(fā)生更新,用戶只需要更新數(shù)據(jù)即可,大大減少流量消耗。

大量使用WebP格式圖片

在這次改版中,很多的圖片我們都使用了WebP格式來(lái)減小圖片大小。

WebP格式,是谷歌開(kāi)發(fā)的一種旨在加快圖片加載速度的圖片格式,圖片壓縮體積大約只有JPEG的2/3,并能節(jié)省大量的服務(wù)器帶寬資源和數(shù)據(jù)空間。但WebP的兼容性不太好,目前基本只有Chrome瀏覽器可以支持,不過(guò)這對(duì)我們的首頁(yè)來(lái)說(shuō),使用WebP還是會(huì)有很大的收益,因?yàn)橥ㄟ^(guò)我們的統(tǒng)計(jì)數(shù)據(jù)可知,首頁(yè)Chrome用戶已經(jīng)占到了60%左右。

體驗(yàn)優(yōu)化探索

在努力提升頁(yè)面性能的同時(shí),還要讓頁(yè)面的用戶體驗(yàn)有所提升,這需要我們能站在用戶和前端的角度提出合理的優(yōu)化方案。

高清屏適配方案

人類的社會(huì)在發(fā)展,人類的社會(huì)在進(jìn)步,現(xiàn)如今高清分辨率屏幕的應(yīng)用已經(jīng)越來(lái)越多,高冷的Mac自不必說(shuō),現(xiàn)在許多新型號(hào)的Windows電腦也配備了高清分辨率的顯示器,所以為了提升這一部分用戶的瀏覽體驗(yàn),我們需要在高清屏上啟用高清素材。

但頁(yè)面中素材圖基本都是運(yùn)營(yíng)上傳的,如果傳兩套圖對(duì)運(yùn)營(yíng)來(lái)說(shuō)未免太過(guò)麻煩,但如果只傳一套高清圖,直接展示的話對(duì)非高清屏沒(méi)有必要,會(huì)造成流量損耗。這時(shí)候京東給力的圖片服務(wù)就發(fā)揮作用了。

圖片服務(wù)支持按一定規(guī)則改變URL來(lái)等比縮放圖片,例如原圖是一張800X340的圖片

//img13.360buyimg.com/cms/jfs/t3412/357/1332248120/113691/f29c2f1e/58244d4dN08b89f9e.jpg!q90.webp

我們可以通過(guò)這樣設(shè)置來(lái)得到一樣等比縮放400X170的圖片

//img13.360buyimg.com/cms/s400x170_jfs/t3412/357/1332248120/113691/f29c2f1e/58244d4dN08b89f9e.jpg!q90.webp

這樣的話,運(yùn)營(yíng)同學(xué)只需要上傳一張高清圖片,我們通過(guò)判斷是否高清屏,來(lái)動(dòng)態(tài)改變URL,使用圖片服務(wù)來(lái)得到一張等比縮放的非高清素材,而且CDN會(huì)根據(jù)圖片URL進(jìn)行緩存,也就是說(shuō)只要第一次訪問(wèn)過(guò)縮放的圖片就好,這樣性能也不會(huì)有什么損耗。

強(qiáng)制webkit內(nèi)核渲染

很多國(guó)產(chǎn)瀏覽器都是雙內(nèi)核,例如360、QQ瀏覽器等,而它們都提供了強(qiáng)制使用Webkit內(nèi)核渲染的開(kāi)啟方式,這樣可以讓用戶獲得更好的瀏覽體驗(yàn)。

OpenSearch

現(xiàn)在很多網(wǎng)站都能實(shí)現(xiàn)在瀏覽器搜索框內(nèi)直接調(diào)用網(wǎng)站內(nèi)部搜索的功能,這是通過(guò) OpenSearch 來(lái)做到的,而京東之前一直是沒(méi)有的,這樣顯然是不合適,而且有一些習(xí)慣于使用地址欄搜索的用戶不能滿足。在這次改版中,我們加上了這一功能,使得用戶可以在瀏覽器地址欄就能直達(dá)京東搜索。

使用Icon Font

使用Icon Font可以提升設(shè)計(jì)師的發(fā)揮空間,在頁(yè)面上使用一些特殊字體以提升頁(yè)面的美觀程度,讓頁(yè)面看起來(lái)更具有設(shè)計(jì)感,更加細(xì)膩,從而提升用戶的瀏覽體驗(yàn)。

而且Icon Font兼容性非常好,可以讓不同瀏覽器的用戶獲得一致的瀏覽體驗(yàn),并且通過(guò)字體壓縮工具,壓縮后的字體文件也可以非常小,不會(huì)有太多的性能損耗。

空閑時(shí)間自動(dòng)加載樓層及圖片

前文提到,我們使用了按需加載來(lái)提升頁(yè)面性能,但這樣帶來(lái)的問(wèn)題就是只有當(dāng)用戶滾動(dòng)樓層到瀏覽器視窗內(nèi),樓層才會(huì)開(kāi)始加載,這樣用戶滾動(dòng)得稍微快一點(diǎn)就會(huì)出現(xiàn)很多l(xiāng)oading動(dòng)畫(huà)。


為了減少這種情況的發(fā)生,讓用戶覺(jué)得樓層也加載很快,在不影響頁(yè)面滾動(dòng)、加載性能的前提下我們?cè)谟脩舨僮鞯目臻e時(shí)間自動(dòng)加載剩余的樓層和圖片。

將樓層的加載操作放入一個(gè)隊(duì)列中,我們可以在用戶停止?jié)L動(dòng)操作3s后開(kāi)始自動(dòng)加載這個(gè)隊(duì)列中的樓層,而當(dāng)用戶開(kāi)始滾動(dòng)的時(shí)候清空這個(gè)加載隊(duì)列,停止?jié)L動(dòng)3秒后又重新開(kāi)始加載。通過(guò)這樣處理可以合理利用用戶瀏覽的空閑時(shí)間來(lái)加載頁(yè)面,讓用戶感覺(jué)頁(yè)面加載更快。


var scrollTimer = null;var isScrolling = false;$(window).bind('scroll.loadFloor', function (e) { isScrolling = true; clearTimeout(autoLoadTimer); clearTimeout(scrollTimer); autoLoadingQueue = []; resourceLoader && resourceLoader.pause(); scrollTimer = setTimeout(function () { isScrolling = false; if (pageConfig.idleTimeLoad) { autoLoadTimer = setTimeout(autoLoad, 3000); } }, 200);});function autoLoad () { if (!isScrolling) { runFloorLoadQueue(); }}


頁(yè)面可用性保障和監(jiān)控

災(zāi)備策略

對(duì)于像京東首頁(yè)這種大流量的網(wǎng)站,后端接口可能偶爾會(huì)出現(xiàn)錯(cuò)誤,或者直接掛掉,特別是在雙11這種可能會(huì)達(dá)到流量峰值的時(shí)候,但是不能因?yàn)榻涌诔鲥e(cuò)的原因而使得頁(yè)面顯示出現(xiàn)錯(cuò)誤。這就需要前端來(lái)配合給出一套合理的災(zāi)備方案。

通常,我們通過(guò)接口緩存、超時(shí)、重試來(lái)進(jìn)行災(zāi)備處理。目前首頁(yè)大部分接口、及所有模板請(qǐng)求,在請(qǐng)求成功后都會(huì)存入本地緩存,第二次請(qǐng)求,假如緩存沒(méi)有過(guò)期將直接使用緩存,假如緩存過(guò)期將會(huì)重新請(qǐng)求,而一次正常的請(qǐng)求,都會(huì)經(jīng)過(guò)超時(shí)或異常重試的邏輯,來(lái)保證用戶能盡量訪問(wèn)到正常的數(shù)據(jù),在正常接口無(wú)法獲取數(shù)據(jù)之后又會(huì)有兜底接口來(lái)保障數(shù)據(jù)來(lái)源,這樣的層層保障,很好地保證了頁(yè)面的完整性。而且,針對(duì)所有接口,前端均有數(shù)據(jù)校驗(yàn)邏輯,每一個(gè)后端接口都要經(jīng)過(guò)前端的數(shù)據(jù)校驗(yàn),來(lái)驗(yàn)證接口的可用性,假如接口數(shù)據(jù)異常,前端將主動(dòng)調(diào)用兜底接口來(lái)替代,這樣來(lái)保證頁(yè)面不至于錯(cuò)亂。

綜上所述,首頁(yè)的接口和模板正常請(qǐng)求流程如下


接口請(qǐng)求流程

這樣一套復(fù)雜的流程下,每一個(gè)接口、模板請(qǐng)求都是統(tǒng)一的,所以需要對(duì)此進(jìn)行封裝,以便調(diào)用。首頁(yè)是通過(guò)封裝改造$.ajax來(lái)實(shí)現(xiàn)的,使用$.ajaxPrefilter和$.ajaxTransport方法對(duì)每個(gè)異步請(qǐng)求進(jìn)行捕獲處理,將接口、模板請(qǐng)求的重試、超時(shí)、緩存、兜底調(diào)用等封裝起來(lái),對(duì)調(diào)用者透明,使用起來(lái)變得非常容易,而不需要關(guān)心以上災(zāi)備策略的實(shí)現(xiàn)。


var ajax = require('load_async');// 本質(zhì)就是$.ajax方法ajax({ url: '//f.3.cn/index-floor/?argv=aggr', jsonpCallback: 'jsonpCallbakcAggr', // jsonp回調(diào)函數(shù)名 params: {}, // 參數(shù) needStore: true, // 是否需要緩存 storeSign: '3aad2efsdf', //用戶判斷緩存是否過(guò)期的標(biāo)記 timeout: 3000, //接口超時(shí) times: 2, // 超時(shí)重試次數(shù) backup: '//www.3.cn/bak/aggr', // 兜底接口 dataCheck: function (result) { // 接口數(shù)據(jù)校驗(yàn),校驗(yàn)接口返回?cái)?shù)據(jù),若為true則走正常邏輯,為false則自動(dòng)調(diào)用兜底邏輯 if (result && result.code === 0) { return true; } return false; }});


數(shù)據(jù)統(tǒng)計(jì)驅(qū)動(dòng)改進(jìn)

在這次首頁(yè)改版項(xiàng)目中我們接入了Athena測(cè)速系統(tǒng)用于收集首頁(yè)各種性能以及用戶環(huán)境相關(guān)的數(shù)據(jù),因?yàn)橛辛藬?shù)據(jù)統(tǒng)計(jì),我們才能知道用戶端具體的情況信息,有了數(shù)據(jù)統(tǒng)計(jì),我們才能對(duì)頁(yè)面進(jìn)行實(shí)時(shí)監(jiān)控,有了數(shù)據(jù)統(tǒng)計(jì),我們才能掌握我們做性能優(yōu)化的成果,所有的分析都是要基于數(shù)據(jù)來(lái)進(jìn)行,否則就是在自己在YY了。

目前我們主要收集了,用戶網(wǎng)速、操作系統(tǒng)、瀏覽器分布、分辨率分布等各種信息,同時(shí)對(duì)于頁(yè)面加載情況也有一定的監(jiān)控,如頁(yè)面測(cè)速打點(diǎn)上報(bào)、數(shù)據(jù)接口出現(xiàn)調(diào)用兜底接口的情況上報(bào)、樓層接口失敗導(dǎo)致樓層隱藏的情況上報(bào)等。

通過(guò)以上數(shù)據(jù)統(tǒng)計(jì),我們可以靈活地對(duì)我們的頁(yè)面進(jìn)行優(yōu)化,同時(shí)及時(shí)發(fā)現(xiàn)問(wèn)題,避免損失。例如我們通過(guò)統(tǒng)計(jì)發(fā)現(xiàn)用戶在網(wǎng)速低于一定值時(shí)頁(yè)面樓層隱藏?cái)?shù)增多,這樣我們就可以通過(guò)設(shè)置更長(zhǎng)的超時(shí)時(shí)間來(lái)減少這一情況的發(fā)生,還有就是假如某時(shí)刻開(kāi)始發(fā)現(xiàn)某接口調(diào)用兜底請(qǐng)求數(shù)暴增,可以判定接口出現(xiàn)問(wèn)題而及時(shí)反饋給后端同學(xué)。

更長(zhǎng)遠(yuǎn)的探索

新版首頁(yè)已經(jīng)上線小一個(gè)月了,表現(xiàn)一直還算良好,我們做出的性能以及體驗(yàn)優(yōu)化也得到了體現(xiàn),在此基礎(chǔ)上,我們思考了更多的可以做的工作,來(lái)提升首頁(yè)的表現(xiàn)。

靜態(tài)資源預(yù)加載

首頁(yè)承載著許多頁(yè)面的入口,如頻道頁(yè)還有活動(dòng)頁(yè),在雙11的時(shí)候,首頁(yè)會(huì)有很多直達(dá)活動(dòng)的入口,如果我們能在首頁(yè)預(yù)加載某些重要的活動(dòng)頁(yè)面的資源的話,當(dāng)用戶去訪問(wèn)這些活動(dòng)頁(yè)面就能更加迅速地打開(kāi)瀏覽了。


靜態(tài)資源預(yù)加載

架構(gòu)升級(jí)

jQuery + Seajs或許讓人感到老舊且沮喪,我們考慮在首頁(yè)上漸漸使用一些新的技術(shù),例如去Seajs化,提供更優(yōu)的打包方式,讓頁(yè)面性能進(jìn)一步提升。

中間層探索

目前首頁(yè)雖然差不多實(shí)現(xiàn)了前后端分離,但是首屏這里前后端依然存在耦合,假如前端可以介入到中間層的開(kāi)發(fā),那問(wèn)題就迎刃而解了,接入中間層后,我們還可以將頁(yè)面部分樓層做服務(wù)端渲染,以減少前端渲染的性能損耗,可以在實(shí)現(xiàn)前后端分離的基礎(chǔ)上,讓頁(yè)面性能更好,還是有一定意義的。

相關(guān)文章:

版權(quán)所有 @ 2007-2023上海海淘信息科技有限公司 滬ICP備11050025號(hào)-4