做者:龍朝忠css
京東微信購物首頁(如下簡稱微信首頁)曾經做爲微信購物一級入口(目前替換爲京喜小程序)一直對性能有着極高的要求,本文將介紹微信首頁的一些優化經驗。html
通常來講產品是按如下方式進行迭代的,我認爲循環的起點應該是「收集用戶反饋」,咱們對頁面的優化依據和目標一個重要來源就是用戶的反饋,所以說網頁優化咱們先從網頁監控開始聊起。前端
京東前端監控涉及的系統主要有兩個:測速系統和智能監控平臺。webpack
網頁將各個關鍵節點的測速信息(時間戳)上傳給系統,系統收集信息後對每一個節點按省份、時間、網絡類型、客戶端類型等多個維度進行統計,並提供可視化分析結果,能夠很方便的監控網頁的加載狀況。web
網頁按照約定格式上報信息給系統,系統收集信息後按照預設的分析模式統計分析結果,若分析結果不符合預期還提供給告警功能。canvas
咱們在微信首頁 CSS 加載完成、HTML 加載完成、JS 加載完成、首屏圖片加載完成、第一張圖片加載完成等關鍵節點插入測速點,並根據業務特色對關鍵內容上報智能監控平臺,如查詢首屏 DOM 節點是否存在上報首屏可用率、檢測重要接口返回信息上報接口可用率。這樣咱們就能對微信首頁的運行健康狀況有一個比較全面的瞭解。小程序
這個階段咱們咱們關注網頁的加載速度,咱們選取首屏圖片加載完成時間做爲核心監控點,重點關注 CSS 加載完成時間、HTML 加載完成時間、JS 加載完成時間、第一張圖片加載完成時間。瀏覽器
第一階段咱們的目標是首屏圖片加載完成時間控制在 1000ms 之內,其餘時間越短越好。爲達到這個目的,咱們採起了一下措施。緩存
首屏直出,也就是服務端渲染( SSR ),微信首頁使用的是一個高效的 C++ 模板- CS 模板生成微信首頁首屏內容。性能優化
以上是服務端渲染( SSR )和客戶端渲染( CSR )在瀏覽器中的呈現區別,根據咱們測試系統檢測採用首屏 SSR 後首屏圖片加載完成時間減小了 1200ms 左右,並且體驗更好了。
關鍵渲染路徑( Critical Render Path )簡稱 CRP ,是指一系列在首屏渲染中必須發生事件,優化關鍵渲染路徑就是優先顯示與當前用戶操做有關的內容。
這是一個不太「精確」的概念,主要是關鍵渲染的規定,這和業務息息相關。關鍵渲染一般來講是指首屏渲染(用戶第一眼可見區域)、頁面的核心內容部分(這個也有點抽象)。
根據瀏覽器工做原理,首先瀏覽器是構建內 DOM 樹和 CSSOM 樹,而後將 DOM 樹和 CSSOM 樹合成「渲染樹」,經過渲染樹計算出佈局信息而後渲染到屏幕上。
所以從渲染流程上來講,HTML 和 CSS 確定是阻止網頁首頁渲染的資源,由於沒有它們就不能構建出渲染樹。 JavaScript 由於可能修改 DOM 或 CSSOM ,所以默認狀況下瀏覽器在解析到 script 標籤時會中止 DOM 樹的構建,等 JavaScript 執行完再從 script 標籤位置從新開始構建 DOM ,因此說 JavaScript 也是阻止網頁首頁渲染的資源。
根據關鍵渲染路徑理論,咱們能夠從三個方面去優化網頁:
拆分首屏和非首屏目的是劃分出關鍵資源,咱們定義除底部 tab 以上的的部分爲首屏內容,這部份內容用戶會最早看到,後面的優化措施就是儘可能讓首屏內容儘快展現。
對於非首屏內容採起延遲加載的方式處理。JS、CSS 異步加載 ,圖片資源懶加載(快進入可視區域時加載)。
關鍵渲染路徑長度是指獲取關鍵資源網絡請求次數
對於這塊的優化,咱們採起了一下措施:
對於首屏資源咱們按類別分別做了一下優化處理。
對於 HTML,咱們使用 html-minifier 工具精簡HTML內容,去除沒必要要的空格和換行。
對於 JS,咱們基於 webpack 對其進行 Treeshaking ,使用 webpack 對 JS 進行 treeshaking 依賴 ES2015(ES6) 模塊系統中的靜態結構特性,所以這部分的優化須要對 JS 進行 ES6 改造。
對於 CSS,開發過程當中常常出現某次活動的樣式在活動下線後忘記去掉,到最後不敢輕易去掉,形成很多無用樣式存在。打包的時候咱們使用 purifyCSS 對這種樣式進行刪除。改工具的實現原理能夠開闊爲:將 CSS 選擇器名稱切割成一個個單詞,而後在全部可能用到的文件中查找這些單詞,若單詞在沒有出如今任何地方說明該 CSS 選擇器對應的樣式沒有用到,能夠刪除。
微信首頁因爲歷史的積累,存在很多無用樣式,使用 purifyCSS 工具處理後能節省 58KB 的關鍵資源大小。
對於 JSON 文件 ,首頁內容大都須要運營配置,所以存在大量 JSON 數據,通過終年的積累對性能的消耗已不容忽視,以下面的一個配置的解析就佔用了 200ms。這塊咱們一是推進推進運營刪除過時數據,二是推進優化 JSON 數據接口,接口智能刪除過時數據。
咱們在客戶端檢測當前環境是否支持 WEBP 和 DPG,並提供統一的轉換函數,服務端也提供了相同的功能。
根據咱們實驗對比發現:
一、DPG 格式和 WEBP 格式均有明顯的壓縮效果,壓縮比例平均在60%如下;
二、DPG 壓縮比 WEBP 壓縮的效果稍微更好一些;
三、DPG + WEBP 雙壓縮比單種格式壓縮有更明顯的提高,達到 30%。
這塊包含兩方面的措施,一是咱們在使用工具發佈微信首頁時,對頁面直接依賴的圖片作無損壓縮,這是後圖片大都是設計師給的切圖,切圖存在大量無用的信息,這時候無損壓縮一半能節省一半的大小。
另外一方面是藉助京東圖片服務壓縮圖片,咱們須要按圖片服務要求格式訪問圖片便可得到壓縮處理後的圖片。
根據咱們測試對比,絕大狀況下 MP4 的大小要比 GIF 小不少
這張圖 GIF 大小爲 125KB 轉成 MP4 後變爲 86KB ,減小了 31.2% 。
這張圖 GIF 大小爲 200KB 轉成 MP4 後變爲 90KB ,減小了 55% 。
Preload 是一個新的控制特定資源如何被加載的新的 Web 標準,這是已經在 2016 年 1 月廢棄的 subresource prefetch 的升級版。通常來講,最好使用 preload 來加載你最重要的資源,好比圖像,CSS ,JavaScript 和字體文件。這不要與瀏覽器預加載混淆,瀏覽器預加載只預先加載在HTML中聲明的資源。Preload 指令事實上克服了這個限制而且容許預加載在 CSS 和 JavaScript 中定義的資源,並容許決定什麼時候應用每一個資源。
咱們使用 Preload 加載微信首頁頭部 banner 第一張圖和頭部氛圍圖,這樣能讓用戶更早看到完整的首屏內容。
Preconnect 是 HTTP 請求正式發給服務器前預先執行一些操做,這包括 DNS 解析,TLS 協商,TCP 握手,這消除了往返延遲併爲用戶節省了時間。咱們對頁面中經常使用域名作了 Preconnect 。
DNS prefetching 容許瀏覽器在用戶瀏覽頁面時在後臺運行 DNS 的解析。如此一來,DNS 的解析在用戶點擊一個連接時已經完成,因此能夠減小延遲。能夠在一個 link 標籤的屬性中添加 rel="dns-prefetch" 來對指定的 URL 進行 DNS prefetching。
Link prefetching 假設用戶將請求指定的 url,瀏覽器在空閒的時候獲取資源並將他們存儲在緩存中。
Prerendering 和 prefetching 很是類似,它們都優化了可能導航到的下一頁上的資源的加載,區別是 prerendering 在後臺渲染了整個頁面,整個頁面全部的資源。
對當前頁面性能無提高,可是若瀏覽器支持,對跳轉到的下一頁意義很大。
一直以來,咱們都用「頁面首屏圖片加載時間」這個指標來做爲優化咱們性能的關鍵 KPI。可是此指標對於「頁面白屏時間很長」、「進度條加載慢」、「搜索框、輪播 banner、底部導航三個模塊出來比較慢」幾個體驗問題,是沒法衡量的。即便咱們把「頁面首屏圖片加載時間」這個數據優化的很小,也並不意味着頁面的性能和體驗很好。這說明拿這個來衡量頁面性能遠遠不夠,咱們須要更多維度的性能指標來衡量頁面的性能。另外,「頁面首屏圖片加載時間」是一個複合動做後的數據結果,包含了 css/js 加載和解析,以及圖片的加載和渲染等綜合狀況,並不能很好的指導頁面作性能優化。再者,這個指標並非一個標準指標,跟開發同窗具體的埋點頗有關係,有些頁面還很很差埋點(好比有些內容新人才可見,怎麼算首屏)。綜上來講,咱們須要有更多維度的、更標準的性能指標來描述頁面的性能,並指導頁面作性能優化。
咱們採用 Google 的 RAIL 模型,此模型關注 Web 應用生命週期的四個方面:響應( Response ,響應時間不超過 100ms ),動畫( Animation,10ms 完成一幀),空閒( Idle,空閒時間越多越好),加載( Load,1000ms 內完成加載),並提出以用戶爲中心的性能指標。
當咱們採用以用戶爲中心的性能模型時,咱們確定也須要採用以用戶爲中心的性能指標。
一、首次繪製時間(FP): FP 標記瀏覽器渲染任何在視覺上不一樣於導航前屏幕內容以內容的時間點
二、首次內容繪製時間(FCP): FCP 標記的是瀏覽器渲染來自 DOM 第一位內容的時間點,該內容多是文本、圖像、SVG 甚至 canvas 元素
四、首次有效繪製(FMP):這是一個「模糊」的概念,是指頁面的主要元素開始繪製的時間
五、可交互時間(TTI): 用於標記應用已進行視覺渲染並能可靠響應用戶輸入的時間點。
六、Long Tasks 監控:根據實測,使用支持最好的 Chrome 實驗,得到的監控結果也不太有用,所以 Long Task 監控展現做罷。
第二階段的性能優化基於第一階段的基礎上,爲了能達到 RAIL 模型要求,咱們進一步作了一下事情。
咱們站在用戶的角度,結合京東微信購物首頁流量轉化狀況,分析認爲首頁除了首屏廣告 banner,搜索框和底部導航做爲用戶使用頻率最高的幾個模塊應該提早渲染。並以首屏廣告 banner 做爲首次有效繪製。
對於搜索框,以前須要加載 3 個 JS 請求和 1 個 CSS 請求才能渲染出來,導致搜索框的渲染嚴重滯後。咱們把以前經過 JS 渲染的 DOM 直接以頁面片形式引入,並將 CSS 樣式內聯,這樣搜索框能在首屏加載時就顯示出來,而後咱們將 3 個 JS 文件合併成一個,這樣就加快了搜索框的初始化。
對於底部導航依賴了一個獨立的 CSS 文件,並且在很靠下的位置,咱們把底部導航的代碼提早到搜索框的下面,並將樣式內聯。
動畫是形成頁面卡頓的重要元兇之一,尤爲是是用 setInterval 實現的動畫,容易形成丟幀現象。所以咱們用 requestAnimationFarme 代替 setInterval ,解決了部分機型動畫卡頓問題 。
當直接監聽頁面滾動時間時,因爲滾動事件觸發頻率很高,即便一個簡單的 handler 函數也會形成大量的開銷。所以咱們對滾動事件作了節流,只容許一個函數在 X 毫秒內執行一次,只有當上一次函數執行後過了你規定的時間間隔,才能進行下一次該函數的調用。
爲了實現圖片 DOM 渲染時不加載,等到快進入可視區域時加載,咱們須要不聽的觀察圖片是否進入了可視區域。以前咱們作法是開啓定時任務,無限循環查詢 img 標籤是否在可視區,很容易生成 Long Task,形成頁面響應遲鈍。
使用最新的 IntersectionObserver 接口代替定時任務,將監控img是否可見的任務交給瀏覽器,能顯著提升效率。
前端技術突飛猛進,網頁的優化也是如此。如經典的雅虎軍規,許多規則到如今仍然具備重要的指導意義,咱們在平常的開發中也仍在嚴格遵照着,可是有一些則該謹慎看待。如進入 HTTP2 時代後,資源的合併就失去了意義,甚至從緩存角度來看會起相反的做用。咱們在微信首頁所作的這些優化措施可能對你的頁面並不適用,但但願能給你一些啓迪。
若是你以爲這篇內容對你有價值,請點贊,並關注咱們的官網和咱們的微信公衆號(WecTeam),每週都有優質文章推送: