<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                &emsp;&emsp;HTTP Archive 在 2022 年關于[多媒體的報告](https://almanac.httparchive.org/en/2022/media)中指出,目前大概有 99.9% 的網站或多或少都會包含點圖像。 &emsp;&emsp;并且高達 70% 的移動頁面和 80% 的桌面頁面的 LCP 指標會受圖像的影響。 &emsp;&emsp;2023-11-27 據統計,合理使用圖像能減少 20% 的帶寬。 &emsp;&emsp;通過這些數據可知,圖像在網頁中占據著舉足輕重的地位,優化圖像,對于網頁性能可以達到立竿見影的效果。 &emsp;&emsp;優化的核心是控制圖像的尺寸,提前、延遲或減少圖像的請求,以及降低對核心 Web 指標的影響。 &emsp;&emsp;本文所用的示例代碼已上傳至[Github](https://github.com/pwstrick/pe)。 ## 一、請求 &emsp;&emsp;以我目前的公司為例,活動頁中圖像的請求數占比最高可達 64%。 &emsp;&emsp;若不做優化處理,那么將直接拉長頁面的白屏時間,體驗將會及其糟糕。 **1)懶加載** &emsp;&emsp;懶加載就是延遲請求的時機,在觸發某個特定條件后,再請求。 &emsp;&emsp;常用的條件是當圖像出現在屏幕內時,觸發請求。 &emsp;&emsp;當頁面很長時,并不需要在頁面首屏加載時就請求所有圖像,而是滾動到圖像所在位置后,再將圖像顯示,如下圖所示。 :-: ![](https://img.kancloud.cn/3e/4b/3e4b1ae3d33f72686a0e04d1461e2538_801x327.jpeg =600x) &emsp;&emsp;目前有 3 種方式來實現懶加載。那么在正式講解之前,需要先了解一下視口的概念。 &emsp;&emsp;視口(viewport)就是下圖中的灰色部分,也就是文檔內容的可視區域,圖中用粗線框住的是瀏覽器的外殼部分(如標簽頁、書簽欄、調試工具等)。 :-: ![](https://img.kancloud.cn/e9/d7/e9d73249de8ff5d65bbae6eb0e2e3412_739x467.png =800x) &emsp;&emsp;先來講解第 1 種懶加載:傳統的 JavaScript 實現,原理就是計算圖像頂部到視口頂部的距離,包括頁面隱藏部分。 &emsp;&emsp;若此距離大于等于當前滾動條的位置(即可視區域),那么就可以認為滿足條件,需要顯示圖像。 &emsp;&emsp;假設滾動條是在body中,那么當前可視區域的范圍如下所示。 ~~~ const viewTop = window.pageYOffset; const viewBottom = window.innerHeight + viewTop; ~~~ &emsp;&emsp;window.[pageYOffset](https://developer.mozilla.org/en-US/docs/Web/API/Window/pageYOffset)表示視口上邊的距離,如果沒有出現垂直方向的滾動條,那么對應屬性的值為 0。window.[innerHeight](https://developer.mozilla.org/en-US/docs/Web/API/Window/innerHeight)表示視口的高度。 &emsp;&emsp;而圖像到視口頂部的距離可以通過[getBoundingClientRect()](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect)的 DOMRect.top 屬性得到,如下所示。 ~~~ const nodeTop = node.getBoundingClientRect().top + viewTop; ~~~ &emsp;&emsp;完整的代碼如下所示, blank.gif 是一張 1\*1 的空白占位圖,data-src 是真實的圖像地址,[scroll](https://developer.mozilla.org/en-US/docs/Web/API/Window/scroll)是滾動條事件。 ~~~html <img src="blank.gif" data-src="cover.jpg" width="100%" /> <img src="blank.gif" data-src="cover.jpg" width="100%" /> <img src="blank.gif" data-src="cover.jpg" width="100%" /> <script> window.addEventListener('scroll', () => { const viewTop = window.pageYOffset; const viewBottom = window.innerHeight + viewTop; // 查詢包含 data-src 自定義屬性的 img document.querySelectorAll('img[data-src]').forEach(node => { const nodeTop = node.getBoundingClientRect().top + viewTop; if (nodeTop >= viewTop && nodeTop <= viewBottom) { node.src = node.dataset.src; } }); }); </script> ~~~ &emsp;&emsp;當前只是為了做演示,兼容性和性能方面并未做深入優化,可以參考市面上成熟的懶加載庫,例如[Layzr.js](https://github.com/callmecavs/layzr.js)。 &emsp;&emsp;接下來講解第 2 種通過[Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)實現懶加載。 &emsp;&emsp;Intersection Observer 提供了一種異步的對目標元素與視口是否相交的檢測方法,即檢測目標元素是否在可視區域中。 &emsp;&emsp;示例代碼如下,省去了位置計算的邏輯,通過 isIntersecting 屬性就能判斷元素的可見性。 ~~~ const observer = new IntersectionObserver((entries, observer) => { entries.forEach((entry) => { // 不在可視區域內就返回 if (!entry.isIntersecting) return; const img = entry.target; img.src = img.dataset.src; observer.unobserve(img); // 取消監控 }); }); document.querySelectorAll("img[data-src]").forEach((node) => { observer.observe(node); }); ~~~ &emsp;&emsp;最后講解第 3 種懶加載方式:img 元素的[loading](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading)屬性。該屬性會指示瀏覽器當圖像不在可視區域時的加載方式。 &emsp;&emsp;這種方式相比較前兩者,最為簡潔,不需要編寫額外的腳本,示例代碼如下所示。 ~~~html <img src="cover.jpg" loading="lazy" width="300" height="400"/> ~~~ &emsp;&emsp;2022 年有 91.47% 的瀏覽器已支持[loading](https://caniuse.com/loading-lazy-attr)屬性,并且大概有 24% 的網站在使用它,相比去年有 1.4 倍的增長。 &emsp;&emsp;但是其延遲加載的規則,即圖像與視口頂部的距離是多少時開始加載,全部由瀏覽器自行定義。 &emsp;&emsp;注意,在 Chrome 中調試發現,頁面打開有兩三屏的圖像就開始請求了,開始滾動后,剩余的圖像就開始陸續請求。 &emsp;&emsp;并不是說到了圖像的可視區域后,才開始請求。 **2)預加載** &emsp;&emsp;預加載和懶加載正好相反,它是在圖像還沒出現在可視區域時提前請求。 &emsp;&emsp;之前做過一次公司招聘的活動頁,其中會涉及到好多動畫和很多圖像,并且需要在手機中翻頁瀏覽。 &emsp;&emsp;一開始將所有圖像地址直接寫在頁面中,在測試環境就發現打開非常慢(如下圖所示),過了幾十秒后才會出現 Loading 過渡動畫。 :-: ![](https://img.kancloud.cn/c2/f8/c2f865165e1957946cbe225ae1346d70_189x190.jpeg) &emsp;&emsp;于是就對其進行優化,將圖像的默認請求替換成一張空白圖(與之前的懶加載一樣),然后在腳本執行后再將首屏替換成真實地址。 &emsp;&emsp;示例代碼如下,初始化 Image 實例,在觸發 load 事件時執行自定義回調,可以是替換 img 元素地址。 ~~~ function loadImage(url, callback) { const img = new Image(); img.src = url; img.onload = function () { //將回調函數的 this 替換為Image對象 callback.call(img); }; } document.querySelectorAll("img[data-src]").forEach((node) => { loadImage(node.dataset.src, function () { node.src = this.src; }); }); ~~~ &emsp;&emsp;在翻頁時,可以將后面幾頁的圖像進行預加載,然后在翻到那頁后,不會出現等待圖像加載的情況,并且動畫就會更加絲滑和順暢。 **3)Data URI** &emsp;&emsp;img 元素的 src 屬性或 CSS 的 background-image 屬性的值都可以是一個經過 Base64 編碼后 Data URI,這樣能減少額外的HTTP請求。 &emsp;&emsp;Data URI 由協議、MIME 類型(可選)、Base64 編碼設定(可選)和內容組成,格式如下: ~~~ data:[<mime type>][;base64],<data> ~~~ &emsp;&emsp;在實際使用中的代碼片段如下: ~~~ data:image/png;base64,/9j/4AAQSkZJRgAB... ~~~ &emsp;&emsp;Base64 會以每 6 個比特為一個單元,對應某個字符,如果要編碼的字節數不能被 3 整除,就用 0 在末尾補足。 &emsp;&emsp;例如編碼 PW,最后得到的值是 UFc=,計算過程如下圖所示。 :-: ![](https://img.kancloud.cn/b7/58/b758f1d8ca1222a21e527020735d9ae8_1318x319.png) &emsp;&emsp;雖然使用 Data URI 減少了一次 HTTP 請求,但它會讓嵌入的文檔體積膨脹四分之三,影響瀏覽器渲染。 &emsp;&emsp;并且還會降低 Gzip 的壓縮效率,破壞資源的緩存。 &emsp;&emsp;若要使用,需要權衡利弊,盡量考慮小尺寸和低更新頻率的圖像。 ## 二、大小 &emsp;&emsp;大多數頁面至少有一張超過 100 KB 的圖像,而在頁面尺寸排行中,前 10% 的頁面至少有一張接近 1 MB 或更大的圖像。 &emsp;&emsp;因此,壓縮或降低圖像的大小,可以顯著地提升頁面性能。 &emsp;&emsp;2023-11-20 注意,預定義圖像的寬和高,就可以在頁面加載時為圖像預留空間。否則當圖像加載時,頁面的布局就會發生變化。 **1)壓縮** &emsp;&emsp;圖像壓縮分為有損和無損。 &emsp;&emsp;前者會改變圖像本身,減少信息量,降低圖像質量,文件無法還原,但是壓縮效率會比較高。 &emsp;&emsp;后者會優化數據存儲方式,利用算法描述重復信息,文件可以還原,但是壓縮效率比有損低。 &emsp;&emsp;在線壓縮網站[TinyPNG](https://tinypng.com/)采用智能有損壓縮技術對圖像進行處理,在我實際使用時,發現最高可壓縮 70% 以上的大小。 &emsp;&emsp;原理就是通過合并圖中相似的顏色,將 24 位的 PNG 圖像壓縮成小得多的 8 位色值的圖像,并且去掉了不必要的元數據。 &emsp;&emsp;經過壓縮后的圖像,人的肉眼并不會看出與原圖明顯的差異。 &emsp;&emsp;若是要用代碼對圖像進行壓縮,可以采用三種觸發時機。 * 第一種是在圖像上傳到服務器后,通過成熟的第三方 Node 庫(例如[imagemin](https://github.com/imagemin)、[node-ffmpeg](https://github.com/damianociarla/node-ffmpeg)、[sharp](https://github.com/lovell/sharp)等)進行壓縮處理。 * 第二種是在訪問圖像地址時,帶上各類參數,動態的對圖像進行壓縮或裁剪,例如 cover.png?w/100,按比例裁剪成 100 的寬度,高度自適應。 * 第三種是在構建過程中對圖像進行壓縮,壓縮后再上傳到服務器中,例如 webpack 的?[ImageMinimizerPlugin](https://webpack.js.org/plugins/image-minimizer-webpack-plugin/)插件等。 &emsp;&emsp;2023-11-20 動態處理除了支持裁剪、壓縮之外,還可以進行格式轉換、漸進顯示(從模糊到清晰)等操作。 &emsp;&emsp;因為處理需要花費些時間,所以一般在處理之后,就會做持久化的存儲,下次訪問相同參數時,就直接返回處理后的圖像。 &emsp;&emsp;目前市場上成熟的 CDN 服務,都會提供相關的圖像處理功能,若要自己開發,可以將其作為參考。 **2)WebP** &emsp;&emsp;WebP 是由 Google 提供的一種圖像格式,支持無損和有損兩種壓縮。 &emsp;&emsp;官方資料表明,[WebP](https://developers.google.com/speed/webp?hl=zh-cn)比 PNG 格式的圖像小 26%,比 JPEG 格式的圖像小 25~34% 。 &emsp;&emsp;在可以接受有損壓縮的情況下,有損 WebP 也支持透明度,并且其文件大小通常比 PNG 小 3 倍。 &emsp;&emsp;雖然表現如此優秀,但是 2022 年,WebP 格式的使用率只占 8.9%,如下圖所示,GIF、JPEG 和 PNG 仍然是主流。 :-: ![](https://img.kancloud.cn/37/54/3754e65b70595c126547ce2bc77246c8_579x290.png) &emsp;&emsp;阻礙其推廣的一大問題是[兼容性](https://caniuse.com/?search=webp),好在目前 iOS 14 以上已經支持 WebP,不考慮 IE 的話,主流的瀏覽器都已支持 WebP 格式。 **3)響應式** &emsp;&emsp;響應式是指根據屏幕尺寸、像素密度或其它設備特性,動態的請求最符合場景的圖像。 &emsp;&emsp;像素密度(PPI)就是每英寸像素,計量設備屏幕的精細程度,值越高圖像越精細,常見的屏幕有 Retina、XHDPI 等。 &emsp;&emsp;接下來用一個例子來演示不同尺寸的屏幕顯示不同的圖像,首先為 img 元素聲明[srcset](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset)屬性。 &emsp;&emsp;用逗號分隔多個描述字符串,每一段包含圖像地址和寬度或像素密度描述符,注意,此處的寬度是圖像的原始寬度。 &emsp;&emsp;然后再聲明 sizes 屬性,其值就是媒體查詢的條件和圖像的顯示寬度,最后一條描述可以省略條件,如下所示。 ~~~html <img srcset="cover-small.jpg 375w, cover.jpg 2449w" sizes="(max-width: 375px) 375px, 800px" src="cover.jpg" /> ~~~ &emsp;&emsp;cover-small.jpg 的原始寬度是 375px,cover.jpg 的原始寬度是 2449px。 &emsp;&emsp;當設備最大寬度是 375 時,將圖像寬度設為 375px,在 srcset 中鎖定最接近的那張圖像的描述。 &emsp;&emsp;800px 是默認的圖像寬度,當無法滿足條件時,就采用這個值。 &emsp;&emsp;如果在做媒體查詢時不清楚各類屏幕尺寸的閾值,那么可以參考 Bootstrap 的[Containers](https://getbootstrap.com/docs/5.3/layout/containers/)。 &emsp;&emsp;注意,若在 srcset 聲明的是像素密度,那么就不需要再額外聲明 sizes 屬性了。 &emsp;&emsp;在 2022 年,srcset 屬性的使用占比在 34%,size 屬性的使用占比在 13%~19%。 &emsp;&emsp;如果要同時適配特定的屏幕尺寸和像素密度,那么可以通過[picture](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)元素實現響應式。 &emsp;&emsp;下面是一個示例,在[source](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source)元素中,media 是媒體查詢條件,srcset 的功能和 img 元素中的相同。 ~~~html <picture> <source media="(max-width: 375px)" srcset="cover-small.jpg, cover-2x.jpg 2x" /> <img src="cover.jpg" /> </picture> ~~~ &emsp;&emsp;當都不符合條件時,就會采用默認的 img 元素。在 2022 年,picture 元素的使用占比是 7.7%。 &emsp;&emsp;除了響應式圖像,picture 元素還可以用來選擇不同格式的圖像,如下所示。 &emsp;&emsp;當瀏覽器支持 WebP 時,就加載這種格式的圖像,否則就加載后面的默認圖像。 ~~~html <picture> <source type="image/webp" srcset="cover.webp"> <img src="cover.jpg" /> </picture> ~~~ **4)切片** &emsp;&emsp;2023-11-20 圖像切片是指將一張幾百 M 或幾個 G 的大圖分割成若干個小圖的過程,以便于存儲和處理,常用于網絡地圖、圖像拼接等應用中。 &emsp;&emsp;在日常拖動地圖時,就會看到新拖出的部分,會從幾個純色方塊瞬間變成某塊地圖,這其實是一種提升顯示效率和縮短用戶等待的手段。 :-: ![](https://img.kancloud.cn/c5/c1/c5c107cbabb86e7b6ca60d3aff3a1dd9_1458x1064.png =600x) ![](https://img.kancloud.cn/de/96/de9648cf68dc61ae3410bcfc8ac4218a_1244x1048.png =600x) &emsp;&emsp;切片的幾個主要步驟包括:定義切片大小、計算切片數量、切割圖像、存儲切片和加載切片。 ## 三、其他優化 &emsp;&emsp;除了上述兩類比較大的優化之外,還有一些其他的細碎優化,在此節會列舉幾個。 &emsp;&emsp;例如對圖像一個比較簡單而有益的優化是預設寬高,提前占位,就能避免影響 CLS 的計算。 **1)延遲解碼** &emsp;&emsp;圖像解碼是光柵化過程中一個比較耗時的步驟,當圖像越大時,解碼時間就越長。 &emsp;&emsp;那么非合成動畫(即非 CSS3 動畫)就有可能因主線程被阻塞而卡頓。值得一提的是,CSS3 動畫運行在合成線程中,所以不會受其影響。 &emsp;&emsp;HTMLImageElement.[decode()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode)方法可以確保圖像解碼后,再將圖像添加到 DOM 中,如下所示。 ~~~ const img = new Image(); img.src = "cover.jpg"; img.decode().then(() => { document.body.appendChild(img); }); ~~~ **2)失敗處理** &emsp;&emsp;在圖像請求失敗時,對頁面的交互并不會造成影響,但是圖像會裂開,在視覺體驗上比較糟糕。 &emsp;&emsp;為 img 元素注冊 error 事件,就能在錯誤時做糾正處理。 ~~~ document.querySelector("img").addEventListener("error", function () { this.src = "../assets/img/cover-small.jpg"; }); ~~~ &emsp;&emsp;不過,若要想知道究竟是什么原因的錯誤,目前還無法做到。 **3)漸進JPEG** &emsp;&emsp;2023-11-20 JPEG 格式的圖像在呈現的時候,有兩種方式,一種是自上而下掃描式,還有一種就是先呈現模糊圖像,然后逐漸清晰。 &emsp;&emsp;日常看漫畫的時候,因為圖像都會比較大,所以被漸進式的加載會比較明顯。 :-: ![](https://img.kancloud.cn/54/08/540894ef0899ac11e9dec201aa3f9004_545x478.png) &emsp;&emsp;PS 軟件支持將導出的圖像以漸進顯示。據國外某位大神研究得出,漸進式圖像在瀏覽器中的加載會更快。 **4)多域名請求** &emsp;&emsp;2023-11-20 瀏覽器的并發請求數目限制是針對同一域名的。 &emsp;&emsp;同一時間針對同一域名下的請求有一定數量限制,超過限制數目的請求會被阻塞。 :-: ![](https://img.kancloud.cn/fe/db/fedba2f69122b7ea61cdbabedf9bb726_361x445.jpeg) &emsp;&emsp;所以要同時請求大量圖像,就可以為圖像配置多個域名,繞開瀏覽器的并發限制。 &emsp;&emsp;接下來做一個對比,分別是一個域名和兩個域名,分別加載圖片。 :-: ![](https://img.kancloud.cn/66/16/6616142248c85fa4bdf97dd740718216_581x268.jpeg) &emsp;&emsp;當一個域名的時候最多只能并發6個請求,而兩個域名的時候能并發10個請求。 :-: ![](https://img.kancloud.cn/79/ee/79ee57d2626803313b05e631da6f78fd_596x270.jpeg) ## 總結 &emsp;&emsp;本文對圖像的優化進行了系統性的梳理,首先是對請求做優化。 &emsp;&emsp;為了更科學的對圖像進行請求,列出了懶加載、預加載和 Data URI 三種優化方法。 &emsp;&emsp;然后對尺寸做優化,講解了壓縮細節,WebP 格式的特點,以及響應式處理的妙用。 &emsp;&emsp;最后再介紹了幾個同樣也能優化圖像的方法,包括占位、延遲解碼和失敗處理。 ***** > 原文出處: [博客園-前端性能精進](https://www.cnblogs.com/strick/category/2267607.html) [知乎專欄-前端性能精進](https://www.zhihu.com/column/c_1610941255021780992) 已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎閱讀。 ![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200) 推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看