Stories

Detail Return Return

性能優化進階:讓你的移動端網頁1s呈現 - Stories Detail

前言

現在的消費者越來越依賴移動設備來訪問內容和服務,這比以往任何時候都要求更高。當他們權衡您網站上的體驗時,他們不僅將您與您的競爭對手進行比較,還會在使用完後對您的應用進行評級。

但是很多網站給用户帶來的體驗並不太好,以致造成潛在客户流失,所以,性能是留住用户的關鍵

Pinterest 將感知等待時間減少了 40%,這將搜索引擎流量和註冊量增加了 15% 。
原文(國外):https://medium.com/pinterest-engineering/driving-user-growth-with-performance-improvements-cfc50dafadd7

COOK 將頁面平均加載時間減少了 850 毫秒,從而將轉化次數提高了 7%,將跳出率降低了 7%,並將每個會話的頁面增加了 10%。

BBC 發現他們的網站加載時間每增加一秒,他們就會失去 10% 的用户。
原文(國外):https://www.creativebloq.com/features/how-the-bbc-builds-websites-that-scale

當 AutoAnything 將頁面加載時間減少一半時,他們的銷售額增長了 12% 到 13%。
原文:https://www.digitalcommerce360.com/2010/08/19/web-accelerator-revs-conversion-and-sales-autoanything/

零售商 Furniture Village 審核了他們的網站速度,並制定瞭解決他們發現的問題的計劃,導致頁面加載時間降低了 20%,轉化率提高了 10%。

在用户體驗方面,速度至關重要。一項消費者研究表明,對移動速度延遲的壓力反應類似於看恐怖電影或解決數學問題,而且比在零售店結賬時排隊等候的壓力更大!

目前,4G 在全球移動網絡中處於主導地位,所以我們會抱有以下預期:大部分用户都是在通過 4G 網絡訪問您的網頁。因此,我們必須假設每項網絡請求的平均耗時是 100 毫秒。

基於這項假設,我們現在不妨逆向思考一下。如果我們仔細分析瀏覽器與服務器之間的典型通信過程,就會發現網絡本身已經消耗掉了 300 毫秒的時間:一次 DNS 查找(用於將主機名(比如 baidu.com)解析為 IP 地址)、一次網絡往返(用於執行 TCP 握手)以及一次可選的 TLS 連接。所以,留給我們的時間就只有 700 毫秒了。

而要在 1 秒內提供並呈現首屏 (ATF) 內容,並讓用户儘快開始與網頁互動,我們需要明白這幾個核心觀點:

  • 服務器必須在200毫秒內呈現相應內容
    服務器響應時間就是在除去網絡傳輸時間之後,服務器返回初始 HTML 所花費的時間。因為我們剩下的時間實在太少了,所以這個時間應該控制在最低限度:理想情況下應該保持在 200 毫秒以內,而且越少越好!
  • 應儘可能減少重定向次數
    額外的 HTTP 重定向可能會增加一次或兩次額外的網絡往返(如果需要再次查找 DNS 的話就是兩次),這在 4G 網絡上將會導致數百毫秒的額外延遲。因此,我們需要儘可能減少重定向次數,而且最好完全消除重定向。儘可能避免重定向到“m.”網址。
  • 應儘可能減少首次呈現內容所需的網絡往返次數
    鑑於 TCP 評估連接狀況的方式(即 TCP 慢啓動),新的 TCP 連接無法立即使用客户端和服務器之間的全部有效帶寬。因此,在通過新連接進行首次往返的過程中,服務器最多隻能發送 10 個 TCP 數據包(約 14KB),然後必須等待客户端確認已收到這些數據,才能增大擁塞窗口並繼續發送更多數據。
    另外還需注意的是,10 個數據包 (IW10) 這一限值源自 TCP 標準的最近一次更新:我們應確保自己的服務器已升級到最新版本,以便能夠充分利用這次更新。否則,這一限值可能會降低到 3-4 個數據包!
    考慮到 TCP 的這種行為,我們需儘可能減少為傳輸必要數據(以完成網頁的首次呈現)而需進行的網絡往返的次數。理想情況下,ATF 內容應小於 98KB,這樣瀏覽器才能在 3 次網絡往返之後即可顯示網頁內容,以便為服務器響應延遲和客户端呈現留出充足的時間預算。
  • 避免在首屏內容中包含會阻止內容呈現的外部 JavaScript 和 CSS
    瀏覽器必須先解析網頁,然後才能將其呈現給用户。如果瀏覽器在解析過程中遇到非異步或阻止呈現的外部腳本,則必須停止解析並且下載相應資源
    所以用於呈現首屏內容的 JavaScript 和 CSS 應內嵌到網頁中,而用於為網頁增添附加功能的 JavaScript 或 CSS 應在 ATF 內容呈現完畢後再開始加載。
  • 為瀏覽器佈局和呈現預留時間(200 毫秒)
    由於移動設備的運行速度和網頁的複雜程度。我們至少需要為瀏覽器的開銷預留 200 毫秒的時間。
  • 優化 JavaScript 的執行及呈現用時

如何在項目中進行優化

相信那些常見的優化方式,例如CDN,或優化代碼和利用瀏覽器http緩存等老生常談的東西,大家已經耳熟能詳。在這裏給大家介紹一些不同的東西,希望能給大家帶來更多的認識,提升網站的性能。

選擇正確的圖像格式

圖像格式一般分為矢量和光柵兩種,這兩種尤其在放大後有不同的表現。
 title=
矢量格式非常適合由簡單幾何形狀(如徽標、文本或圖標)組成的圖像。它們在每種分辨率和縮放設置下都能提供清晰的結果,是高分辨率屏幕和需要以不同尺寸顯示同樣效果的理想格式。

但是,當場景複雜時(例如,照片),矢量格式就會出現不足。例如SVG在這種情況下的標記量可能高得令人望而卻步,並且輸出可能仍然不“逼真”。所以就需要使用光柵圖像格式,例如 PNG、JPEG、WebP 或 AVIF。

光柵圖像不具有分辨率或縮放相同的特性——當放大光柵圖像時,會看到鋸齒狀和模糊的圖形。因此,我們需要以不同的分辨率保存多個版本的光柵圖像,以便為用户提供最佳體驗。

不同的光柵圖像格式,所支持的特性也不同:
 title=

用視頻替換動畫 GIF

不知道你們是否在開發工具中檢查過GIF,可能會發現非常龐大。所以通過將大型 GIF 轉換為視頻,可以節省大量用户帶寬。
在這裏插入圖片描述
可以看到,gif原為3.7M,轉為mp4格式後551K,而轉為webm後,僅有341K。

當圖像出現在視圖時才開始加載

如果你之前寫過延遲加載代碼,那麼可能通過使用scroll 或 resize 之類的事件處理程序來完成。雖然這種方法在各大瀏覽器之間的兼容性最好,但現代瀏覽器提供了一種性能更高、效率更高的方法,通過 Intersection Observer API 來完成檢查元素可見性的工作。

我們使用 JavaScript 來檢查它們是否位於視區。若位於視區,則會向它們的 src (有時是srcset )屬性填充那些指向所需圖像內容的 URL。

<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">
document.addEventListener("DOMContentLoaded", function() {
  var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          lazyImage.srcset = lazyImage.dataset.srcset;
          lazyImage.classList.remove("lazy");
          lazyImageObserver.unobserve(lazyImage);
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });
  } else {
    // Possibly fall back to event handlers here
  }
});

以上是 img 標籤的延遲加載方式, 如果用CSS background-image 屬性(和其他屬性)做延遲加載也是和以上同理。

當視頻出現在視圖時才開始加載

<video class="lazy" autoplay muted loop playsinline width="610" height="254" poster="one-does-not-simply.jpg">
  <source data-src="one-does-not-simply.webm" type="video/webm">
  <source data-src="one-does-not-simply.mp4" type="video/mp4">
</video>

延遲加載 video 元素時,和延遲加載圖像一樣,不同的是,視頻需要迭代所有子 source 元素,並將其 data-src 屬性改為 src 屬性。完成後,需要通過調用元素的 load 方法來觸發視頻的加載,此後,媒體將根據 autoplay 屬性開始自動播放。

  • 對於不自動播放的視頻 需要在 <video> 元素上指定 preload 屬性,preload="none";
  • 對於代替動畫 GIF 的視頻 有一點需要注意,要在 iOS 中自動播放,playsinline 必不可少;

儘可能減少瀏覽器重排

很多種操作HTML的行為 都可觸發重排。例如:調整瀏覽器窗口的大小、使用涉及計算出的樣式的 JavaScript 方法、在 DOM 中添加或移除元素,以及更改某個元素的類,等等。需要注意的是,這些操作導致的重排用時可能比想象的要長。

下面是一些簡單的準則,可幫助我們儘可能縮短在網頁中進行重排的用時:

  • 減少不必要的 DOM 深度。在 DOM 樹中的一個級別進行更改可能會致使該樹的所有級別(上至根節點,下至所修改節點的子級)都隨之變化。這會導致花費更多的時間來執行重排。
  • 儘可能減少 CSS 規則的數量,並移除未使用的 CSS 規則。
  • 如果您想進行復雜的渲染更改(例如動畫),請在流程外執行此操作。您可以使用 position-absolute 或 position-fixed 來實現此目的。
  • 避免使用不必要且複雜的 CSS 選擇器(尤其是後代選擇器),因為此類選擇器需要耗用更多的 CPU 處理能力來執行選擇器匹配。

@babel/preset-env

如果是@babel/preset-env7.10 或babel 8 以下,可以啓動 bugfixes: true
babel將多個 JavaScript 語法功能分組到集合中,並根據指定的目標瀏覽器啓用/禁用它們。雖然這很好用,但當目標瀏覽器只有一個特性的錯誤時,整個語法特性集合就會被轉換。這通常會導致轉換後的代碼比必要的要多。所以可以通過此選項啓用優化。

避免在瀏覽器中使用CommonJS

CommonJS起初為服務端設計,在一般情況下更難優化,因為它們比 ES 模塊的動態程度更高。為確保捆綁程序和壓縮器能夠成功優化應用程序,請避免依賴 CommonJS 模塊,並在整個應用程序中使用 ECMAScript 模塊語法。

DNS-prefetch

當瀏覽器從(第三方)服務器請求資源時,必須先將該跨域域名解析為 IP 地址,然後瀏覽器才能發出請求。此過程稱為 DNS 解析。

而 DNS 解析可以導致請求增加明顯的延遲。對於打開了與許多第三方的連接的網站,此延遲可能會大大降低加載性能。

DNS 預獲取是嘗試在請求資源之前解析域名(這可能是後面要加載的文件,也可能是用户嘗試打開的鏈接目標)。DNS 緩存可以幫助減少此延遲,

 <head>
    <link rel="dns-prefetch" href="https://fonts.gstatic.com/">
    <!-- and all other head elements -->
  </head>

可以看到,百度也採用了這種優化技術

在這裏插入圖片描述

注意事項:

  • dns-prefetch 僅對跨域域上的 DNS 查找有效,因此請避免使用它來指向本站點或域
  • 考慮將 dns-prefetch 與 preconnect(預連接)提示配對

    dns-prefetch 僅執行 DNS 查找,而preconnect 會建立與服務器的連接。如果站點是通過 HTTPS 服務的,則此過程包括 DNS 解析,建立 TCP 連接以及執行 TLS 握手。將兩者結合起來可提供進一步減少跨域請求的感知延遲的機會

      <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
      <link rel="dns-prefetch" href="https://fonts.gstatic.com/">

    如果頁面需要建立與許多第三方域的連接,預先連接會適得其反。 preconnect 提示最好僅用於最關鍵的連接。對於其他的,只需使用dns-prefetch。

    一些資源(如字體)以匿名模式加載。在這種情況下,應使用預連接提示設置 crossorigin 屬性

設置Link標籤優先級

  • <link rel="preload">通知瀏覽器需要一個資源作為當前導航的一部分,並且應該儘快開始獲取它。
  • <link rel="preconnect">通知瀏覽器頁面打算建立與另一個來源的連接,並且您希望該過程儘快開始。
  • <link rel="prefetch"> 和 <link rel="preload">有點不同,因為它不會讓關鍵的事情發生得更快;相反,如果有機會,它會嘗試讓一些非關鍵的事情更早發生。

快速播放音頻&視頻預加載

播放開始得越快,意味着觀看您的視頻或收聽您的音頻的人越多。接下來將探討一些實用的技術,可以使用這些技術主動預加載資源,來加速音頻和視頻播放。

 title=

  • 視頻預加載屬性

使用視頻 preload 屬性向瀏覽器提供有關要預加載多少信息或內容的提示。這意味着媒體源擴展 (MSE) 與 preload 不兼容。
僅當初始 HTML 文檔已完全加載並解析(例如 DOMContentLoaded 事件已觸發)後才會開始獲取資源,而在實際獲取資源時才會觸發與之不同的 load 事件。
 title=
將 preload 屬性設置為 metadata 表示用户不需要視頻,但需要獲取其元數據(尺寸、曲目列表、持續時間等)。請注意,從 Chrome 64 開始,preload 的默認值是 metadata 。 (先前是 auto)。設置為 auto 表示瀏覽器可以緩存足夠的數據,無需停止進一步緩衝即可完成播放。

不過也有一些需要注意的地方。由於這只是一個提示,因此瀏覽器可能會完全忽略 preload 屬性。在撰寫本文時,以下是在 Chrome 中應用的一些規則:

  1. 啓用流量節省程序後,Chrome 會將 preload 值強制為 none 。
  2. 在 Android 4.3 中,由於 Android 缺陷,Chrome 會將 preload 值強制為 none。
  3. 在蜂窩連接(2G、3G 和 4G)上,Chrome 會將 preload 值強制為 metadata 。

如果網站在同一域中包含許多視頻資源,建議將 preload 值設置為 metadata 或定義 poster 屬性並將 preload 設置為none 。這樣,可避免達到同一域的最大 HTTP 連接數(根據 HTTP 1.1 規範為 6 個),超過6個會掛起資源加載。如果視頻不是頁面的重點,設置後也會提高頁面速度。

  • 鏈接預加載
    鏈接預加載是一種聲明性獲取,可強制瀏覽器請求資源,而不會阻止 load 事件,同時頁面也在下載。通過 <link rel="preload"> 加載的資源存儲在本地瀏覽器中,並且在 DOM、JavaScript 或 CSS 中顯式引用它們之前,它們實際上是靜止的。
     title=

    以下是如何在您的網站上預加載完整視頻的方法,這樣當您的 JavaScript 要求獲取視頻內容時,它會從緩存中讀取,因為瀏覽器可能已經緩存了該資源。如果預加載請求尚未完成,則會進行常規網絡獲取。

    // 如果 as 預加載鏈接值為 video,預加載資源就會被視頻元素消耗。
    // 如果它是一個音頻元素,即為 as="audio" 。
    <link rel="preload" as="video" href="https://cdn.com/small-file.mp4">
    
    <video id="video" controls></video>
    
    <script>
    // 隨後,在滿足某些條件後,將視頻源設置為
    // 預加載視頻 URL。
    video.src = 'https://cdn.com/small-file.mp4';
    video.play().then(() => {
      // 如果預加載視頻 URL 已緩存,即會立即開始播放。
    });
    </script>
    
    // 建議僅將此法用於小型媒體文件(小於 5MB)。
    

優化第三方JavaScript

第三方 JavaScript 通常是指嵌入在您網站中的腳本,第三方腳本可以提供強大的功能,但它們還會影響隱私、安全和頁面行為——而且它們對性能的影響尤其大。例如:

  • 觸發額外的網絡請求
  • 拉入未優化的圖像和視頻
  • HTTP緩存不足,導致頻繁獲取網絡資源
  • 服務器資源壓縮不足
  • 由不同的第三方嵌入引入的框架和庫的多個實例

由於使用第三方 JavaScript 通常是不可避免的,但可以採取一些措施來最大程度地減少不利影響:

  • 在選擇第三方資源時,請選擇那些發送最少代碼同時仍能提供所需功能的資源
  • 延遲加載第三方腳本
  • 不要使用來自兩個不同供應商的相同功能。比如兩個標籤管理器或兩個選擇器。
  • 定期審核並清除多餘的第三方腳本。
  • 自託管第三方腳本。

基於網絡質量自適應

根據網絡條件,加載網站可能是一種非常不同的體驗。當使用快速網絡時,一切通常都很順利,但是當在旅途中使用有限的網絡和不穩定的連接,這些情況下使用筆記本電腦時,情況就不同了。

可以通過多種方式來改善用户體驗:

  • 根據用户的網絡在提供高清和低分辨率內容之間切換。
  • 決定是否預加載資源。
  • 當用户連接速度較慢時,推遲上傳和下載。
  • 如果網絡質量不足以加載應用程序和使用功能,請啓用離線模式。

我們可以通過瀏覽器提供的這個Api來實現:

 title=
這是它的使用描述:
 title=

然後配合navigator.connection.addEventListener('change', doSomethingOnChange); 實現自適應。

怎麼測試網頁速度?

  1. Lighthouse 可以通過獲取一個 URL 並針對該頁面運行一系列審核,生成有關該頁面執行情況的報告。有多種運行 Lighthouse 的方法,包括從 Chrome DevTools 中安裝插件來審核頁面。
  2. PageSpeed Insights 提供有關頁面的實驗和現實場景的數據。它使用 Lighthouse 收集和分析有關頁面的實驗數據,而真實的現場數據基於 Chrome 用户體驗報告數據集合。
  3. Chrome 開發者工具 是直接內置在 Google Chrome 瀏覽器中的 Web 開發者工具。允許分析頁面運行時、識別和調試性能。

建立公司的性能文化

公司內的所有部門通常都依賴於用來轉化用户的網站,無論是為了創收還是其他目的。但殘酷的事實是:如果網頁加載時間過長,人們是不會留下來查看它們的,無論網頁多麼漂亮。所以我們需要在公司建立起產品的性能文化。

以下是幾點建議,可供參考:

  • 優化網站資源
    開發人員可以使用各種媒體優化技術來加快媒體加載速度。UI 可以創建有助於將頁面重量保持在約定水平以下的圖像。
    並致力於研究開發團隊如何通過各種資源優化技術(例如資源壓縮、響應式圖像、圖像大小調整、延遲加載、緩存和服務器優化)更快地加載顯示資源。

    • 跟蹤站點上的哪些頁面具有最高的加載時間和最高的頁面權重
    • 準備一份清單,列出哪些頁面高於了其本身的權重水平,並進行改進。
    • 為設計師準備一份績效預算,比如:所有頁面的圖像重量設置為 1 MB之內(如果品牌對轉化非常重要,可以設置為 1.5 MB)
  • 在 Lighthouse 分數低於設置的閾值(例如 < 96/100)時不允許合併拉取請求
  • 提煉和精簡業務
    與網站速度相關的一個常見誤解是,這只是開發團隊的責任。現實情況是,一個快速的網站需要多個部門一起協作。
  • 定期跟蹤提升速度後帶來的轉化,並及時改進

參考鏈接:
https://developer.mozilla.org/zh-CN/docs/Web/Performance/dns-prefetch
https://web.dev/fast/#prioritize-resources
https://developers.google.com/speed/docs/insights/mobile

Add a new Comments

Some HTML is okay.