【學習圖片】13.自動壓縮和編碼

前端小智發表於2023-03-13
本文首發於微信公眾號:大遷世界, 我的微信:qq449245884,我會第一時間和你分享前端行業趨勢,學習途徑等等。
更多開源作品請看 GitHub https://github.com/qq449245884/xiaozhi ,包含一線大廠面試完整考點、資料以及我的系列文章。

本課程中的所有語法——從影像資料的編碼到支援響應式影像的資訊密集標記語言——都是機器與機器之間通訊的方法。

客戶端瀏覽器與伺服器相互通訊有許多方式。響應式影像標記語言(尤其是srcsetsizes)使用較少的字元描述了大量資訊。

不管是好是壞,這種簡潔是設計出來的:讓這些語法不那麼簡練,從而讓開發者更容易理解,可能會讓瀏覽器更難解析它們。字串的複雜性越高,解析器出錯的可能性就越大,或者在不同的瀏覽器之間出現無意的行為差異。

image.png

然而,能使這些主題感到如此可怕的特性也能為你提供解決方案:機器容易閱讀的語法更容易被它們編寫。作為Web使用者,我們肯定遇到過許多自動化影像編碼和壓縮的示例:透過社交媒體平臺、內容管理系統(CMS)甚至電子郵件客戶端上傳到Web的任何影像幾乎都會透過一個系統來調整大小、重新編碼和壓縮。

同樣地,無論是透過外掛、外部庫、獨立構建過程工具還是負責使用客戶端指令碼,響應式影像標記語言都很容易適應自動化。

這是圍繞自動化影像效能的兩個主要問題:管理影像的建立--它們的編碼、壓縮和你用來填充srcset屬性的備用來源--以及生成我們面向使用者的標記。在本模組中,你將瞭解一些管理影像的常用方法,作為現代工作流程的一部分,無論是作為開發過程中的一個自動化階段,還是透過為你的網站提供動力的框架或內容管理系統,或者透過專門的內容交付網路幾乎完全抽象化。

自動化壓縮和編碼

我們不太可能花時間手動確定每個單獨圖片的最佳編碼和壓縮級別的位置,也不會想這麼做。儘管保持影像傳輸尺寸儘可能小很重要,但為每個圖片微調壓縮設定並重新儲存備選源對於我們日常工作中會增加很多時間。

正如你在閱讀各種圖片格式和壓縮型別時學到的,影像的最有效編碼始終取決於其內容,正如你在響應式圖片中學到的,你所需的備選尺寸將取決於這些影像在頁面佈局中所佔據的位置。在現代工作流中,你將綜合考慮這些決策,而不是單獨決定——為影像確定一組合理的預設值,以最好地適應它們所用於的上下文環境。

在為一組照片影像選擇編碼時,AVIF在質量和傳輸尺寸方面是最佳選擇,但其支援有限,WebP提供了一個最佳化的現代備選方案,而JPEG是最可靠的預設值。我們需要為在頁面佈局中佔據側邊欄的影像製作的備選尺寸與我們在最高斷點下佔據整個瀏覽器視口的影像大不相同。壓縮設定需要考慮到多個結果檔案中的模糊和壓縮偽影,這樣就沒有太多的空間來為每個影像刻意減少每個可能的位元組,而需要換取更靈活和可靠的工作流程。

至於處理本身,有大量的開源影像處理庫提供批次轉換、修改和編輯影像的方法,它們競爭速度、效率和可靠性。這些處理庫允許你一次性對整個目錄中的影像應用編碼和壓縮設定,而無需開啟影像編輯軟體,並以一種方式保留原始影像源,以便在需要時調整這些設定。它們旨在執行在各種上下文環境中,從本地開發環境到 Web 伺服器本身,例如,針對壓縮的 Node.js 的 ImageMin 可以透過一系列外掛擴充套件以適應特定應用程式,而跨平臺的 ImageMagick 和基於 Node.js 的 Sharp 從一開始就帶有驚人的功能。

這些影像處理庫使開發者有可能建立專門用於無縫最佳化影像的工具,作為你的標準開發流程的一部分--確保你的專案將始終以儘可能少的開銷引用生產就緒的影像源。

本地開發工具和工作流程

像Grunt、Gulp或Webpack這樣的任務執行器和捆綁器可以用來最佳化影像資產和其他常見的效能相關的任務,如CSS和JavaScript的最小化。為了說明這一點,讓我們來看看一個相對簡單的用例:你的專案中的一個目錄包含了十幾張攝影圖片,打算用在一個面向公眾的網站上。

首先,你需要確保這些圖片的編碼一致、高效。正如你在前面的模組中所學到的,WebP在質量和檔案大小方面都是攝影圖片的有效預設值。WebP得到了很好的支援,但不是普遍支援的,所以你也要包括一個漸進式JPEG形式的回退。然後,為了利用srcset屬性來有效地交付這些資產,你需要為每種編碼製作多種備用尺寸。

如果使用影像編輯軟體完成此操作,這將是一項重複且耗時的瑣事,但是像Gulp這樣的任務執行器就是為自動化這種重複性工作而設計的。gulp-responsive外掛(利用Sharp)是眾多選項之一,它們都遵循類似的模式:收集源目錄中的所有檔案,重新編碼它們,並根據你在影像格式和壓縮中瞭解到的相同標準化的“質量”簡寫進行壓縮。然後將結果檔案輸出到我們定義的路徑中,準備在面向使用者的img元素的src屬性中引用,同時保留原始檔案。

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.webp = function() {
  return src('./src-img/*')
    .pipe(respimg({
      '*': [{
        quality: 70,
        format: ['webp', 'jpeg'],
        progressive: true
      }]
  }))
  .pipe(dest('./img/'));
}

有了這樣一個過程,如果專案中有人不小心將一張編碼為大量真彩色PNG的照片新增到包含你的原始影像源的目錄中,就不會對生產環境造成任何傷害--無論原始影像的編碼如何,這項任務將產生一個高效的WebP和可靠的漸進式JPEG回退,而且壓縮級別可以很容易地進行即時調整。當然,這個過程也確保你的原始影像檔案將被保留在專案的開發環境中,這意味著這些設定可以在任何時候調整,只有自動輸出被覆蓋。

為了輸出多個檔案,你要傳遞多個配置物件--除了增加一個寬度和一個畫素值,其他都是一樣的。

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.default = function() {
  return src('./src-img/*')
    .pipe(respimg({
      '*': [{
              width: 1000,
              format: ['jpeg', 'webp'],
              progressive: true,
              rename: { suffix: '-1000' }
            },
            {
              width: 800,
              format: ['jpeg', 'webp'],
              progressive: true,
              rename: { suffix: '-800' }
            },
            {
              width: 400,
              format: ['jpeg', 'webp'],
              progressive: true,
              rename: { suffix: '-400' },
          }]
        })
    )
    .pipe(dest('./img/'));
}

在上面的例子中,原始影像(monarch.png)超過了3.3MB。這個任務產生的最大檔案(monarch-1000.jpeg)約為150KB。最小的,monarch-400.web,只有32KB。

[10:30:54] Starting 'default'...
[10:30:54] gulp-responsive: monarch.png -> monarch-400.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-800.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-400.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-800.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.webp
[10:30:54] gulp-responsive: Created 6 images (matched 1 of 1 image)
[10:30:54] Finished 'default' after 374 ms

當然,你要仔細檢查結果,看是否有明顯的壓縮偽影,或者可能增加壓縮量以節省更多的費用。由於這項任務是非破壞性的,這些設定可以很容易地被改變。

而且具有韌性的過程,這個工具可以將您對高效能影像資源的知識無縫地應用於整個專案,無需任何手動干預。

響應式影像實踐

填充srcset屬性通常是一個簡單的手動過程,因為該屬性實際上只捕捉在生成源時已經完成的配置資訊。在上述任務中,我們已經確定了檔名和寬度資訊,這些資訊將用於屬性:

srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w"

請記住,srcset屬性的內容是描述性的,而不是規定性的。只要每個源的寬高比一致,過載srcset屬性並不會造成任何傷害。一個srcset屬性可以包含伺服器生成的每個備用影像的URI和寬度,而不會引起任何不必要的請求。我們提供給渲染影像更多的備選源,瀏覽器就能更有效地最佳化請求。

正如在響應式影像中所學到的,我們將需要使用<picture>元素來無縫地處理WebP或JPEG回退模式。在這種情況下,將與srcset一起使用type屬性。

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

正如你所學到的,支援WebP的瀏覽器將識別type屬性的內容,並選擇該<source>元素的srcset屬性作為影像候選列表。不支援image/webp作為有效媒體型別的瀏覽器將忽略這個<source>,並使用內部<img>元素的srcset屬性。

在瀏覽器支援方面還有一點需要考慮:不支援任何響應式影像標記的瀏覽器仍需要一個回退,否則在特別舊的瀏覽環境中可能會出現損壞的影像。因為這些瀏覽器都會忽略<picture><source>srcset,所以我們需要在內部<img>src屬性中指定一個預設來源。

由於縮小影像在視覺上是無縫的,而JPEG編碼是普遍支援的,因此選擇最大的JPEG是一個明智的選擇。

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img src="filename-1000.jpg" srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

sizes可能會更加難以處理。sizes是上下文相關的,我們無法在不知道影像在渲染布局中所佔空間量的情況下填充該屬性。為了使請求儘可能有效率,在終端使用者發出請求之前,我們的標記中就需要有一個準確的sizes屬性,以便在請求樣式規則和頁面佈局之前。完全省略sizes不僅違反了HTML規範,而且會導致預設行為等效於sizes="100vw"——告訴瀏覽器該影像僅受到視口本身的限制,從而選擇最大的候選來源。

與任何特別繁瑣的Web開發任務一樣,已經建立了許多工具來抽象出手寫sizes屬性的過程。respImageLint是一個絕對必要的程式碼片段,旨在審查你的sizes屬性的準確性並提供改進建議。它作為書籤工具執行——您、在指向包含影像元素的完全渲染頁面時在瀏覽器中執行。在瀏覽器完全理解頁面佈局的上下文中,它還幾乎可以準確地感知在每個可能的視口大小下影像在佈局中所佔用的空間。

image.png

一個用於提示你的尺寸屬性的工具當然是有用的,但作為一個全盤生成這些屬性的工具,它的價值甚至更大。如你所知,srcsetsizes 語法旨在以一種視覺上無縫的方式最佳化影像資產的請求。雖然不應該在生產中使用,但在本地開發環境中處理頁面佈局時,預設的尺寸佔位符值為100vw是完全合理的。一旦佈局風格到位,執行respImageLint將為你提供量身定做的尺寸屬性,你可以將其複製並貼上到你的標記中,其詳細程度遠遠超過手工書寫。

image.png

儘管由伺服器呈現的標記發起的影像請求過於迅速,以至於 JavaScript 無法生成客戶端大小屬性,但如果這些請求是由客戶端發起的,則不能應用同樣的推理。例如,Lazysizes 專案允許您完全推遲影像請求,直到佈局已經建立,允許 JavaScript 為我們生成大小值-這對您來說非常方便,併為您的使用者提供了最高效的請求保證。請記住,這種方法意味著犧牲了伺服器呈現的標記和瀏覽器內建的速度最佳化的可靠性,只有在頁面呈現後才啟動這些請求將對您的 LCP 評分產生過度負面影響。

當然,如果你已經依賴於客戶端渲染框架,例如 React 或 Vue,則您已經在承擔這種債務-在這些情況下,使用 Lazysizes 意味著您的大小屬性可以幾乎完全抽象化。更好的是:隨著 lazy loaded images 上 sizes="auto" 獲得共識和本機實現,Lazysizes 將有效地成為新標準化的瀏覽器行為的 polyfill。

原文:https://web.dev/learn/images/automating/

程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

交流

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

相關文章