Tự động nén và mã hoá

Biến việc tạo các nguồn hình ảnh có hiệu suất cao thành một phần liền mạch quá trình phát triển ứng dụng.

Tất cả cú pháp trong khoá học này – từ mã hoá dữ liệu hình ảnh đến mã hoá thông tin chuyên sâu thẻ đánh dấu hỗ trợ hình ảnh thích ứng – là phương pháp để máy móc giao tiếp với máy. Bạn có đã khám phá một số cách để trình duyệt ứng dụng truyền đạt nhu cầu của mình đến máy chủ và máy chủ để phản hồi bằng hiện vật. Mã đánh dấu hình ảnh thích ứng (cụ thể là srcsetsizes) quản lý để mô tả lượng thông tin gây sốc tương đối một vài ký tự. Dù tốt hơn hay tệ hơn, sự ngắn gọn đó là do thiết kế: làm cho các cú pháp này trở nên ngắn gọn hơn và giúp nhà phát triển dễ dàng sử dụng hơn để phân tích cú pháp, có thể khiến trình duyệt khó phân tích cú pháp hơn. Chuỗi càng phức tạp, có thể xảy ra lỗi trình phân tích cú pháp hoặc sự khác biệt ngoài ý muốn về hành vi giữa các trình duyệt.

Cửa sổ mã hoá hình ảnh tự động.

Tuy nhiên, chính đặc điểm khiến các đối tượng này cảm thấy đáng sợ cũng có thể mang lại cho bạn giải pháp: một cú pháp dễ dàng đọc bởi máy là cú pháp mà chúng viết dễ dàng hơn. Bạn gần như chắc chắn đã gặp nhiều ví dụ về chiến dịch tự động mã hoá và nén hình ảnh với tư cách là người dùng web: bất kỳ hình ảnh nào được tải lên web thông qua các nền tảng mạng xã hội, nội dung hệ thống quản lý nội dung (CMS) và thậm chí cả ứng dụng email khách sẽ luôn đi qua một hệ thống có khả năng đổi kích thước, mã hoá lại, và nén chúng.

Tương tự, cho dù thông qua trình bổ trợ, thư viện bên ngoài, công cụ quy trình xây dựng độc lập hay sử dụng tập lệnh phía máy khách một cách có trách nhiệm, mã đánh dấu hình ảnh thích ứng dễ dàng thích ứng với khả năng tự động hoá.

Đó là hai mối quan tâm chính xung quanh việc tự động hoá hiệu suất của hình ảnh: quản lý việc tạo hình ảnh, các phương thức mã hoá, nén và các nguồn thay thế mà bạn sẽ dùng để điền thuộc tính srcset nhằm tạo mã đánh dấu dành cho người dùng. Trong học phần này, bạn sẽ tìm hiểu về một số phương pháp phổ biến để quản lý hình ảnh trong quy trình làm việc hiện đại, cho dù với tư cách là giai đoạn tự động trong quá trình phát triển của bạn, thông qua khung hoặc hệ thống quản lý nội dung hỗ trợ trang web của bạn, hoặc bị loại bỏ gần như hoàn toàn bởi một mạng phân phối nội dung chuyên dụng.

Tự động nén và mã hoá

Bạn khó có thể thấy mình ở vị trí có thể dành thời gian để xác định thủ công mã hoá và mức độ lý tưởng nén cho từng hình ảnh riêng lẻ để sử dụng trong một dự án – bạn cũng không muốn như vậy. Như điều quan trọng là giữ cho kích thước chuyển hình ảnh nhỏ nhất có thể , tinh chỉnh và lưu lại các nguồn thay thế cho mỗi thành phần hình ảnh dành cho một trang web sản xuất. gây trở ngại lớn trong công việc hằng ngày.

Như bạn đã tìm hiểu khi đọc về các định dạng hình ảnh và loại nén khác nhau, cách mã hoá hiệu quả nhất cho một hình ảnh sẽ luôn được quyết định bởi nội dung của hình ảnh và như bạn đã tìm hiểu trong Hình ảnh thích ứng, kích thước thay thế bạn cần cho nguồn hình ảnh sẽ là được chỉ định bởi vị trí mà các hình ảnh đó chiếm trong bố cục trang. Trong quy trình làm việc hiện đại, bạn sẽ tiếp cận những quyết định này xác định một nhóm giá trị mặc định hợp lý cho hình ảnh, chứ không phải từng hình ảnh riêng lẻ, để phù hợp nhất với bối cảnh mà chúng dành cho mục đích sử dụng.

Khi chọn phương thức mã hoá cho thư mục hình ảnh, AVIF là giải pháp vượt trội rõ ràng về chất lượng và kích thước truyền nhưng khả năng hỗ trợ hạn chế, WebP cung cấp tính năng dự phòng hiện đại và được tối ưu hoá, còn JPEG là chế độ mặc định đáng tin cậy nhất. Phương án thay thế kích thước chúng ta cần tạo cho những hình ảnh chiếm thanh bên trong một bố cục trang sẽ khác rất nhiều so với những hình ảnh để chiếm toàn bộ khung nhìn của trình duyệt tại các điểm ngắt cao nhất của chúng tôi. Chế độ cài đặt nén sẽ yêu cầu mắt làm mờ và các cấu phần phần mềm nén trên nhiều tệp kết quả, giúp tiết kiệm không gian để tạo từng byte có thể có từ mỗi hình ảnh nhằm có được quy trình làm việc linh hoạt và ổn định hơn. Tóm lại, bạn sẽ theo dõi cùng một quy trình đưa ra quyết định để hiểu từ khoá học này, viết rất nhiều.

Đối với bản thân quá trình xử lý, có một số lượng lớn thư viện xử lý ảnh nguồn mở cung cấp các phương pháp chuyển đổi, sửa đổi và chỉnh sửa hình ảnh hàng loạt, cạnh tranh về tốc độ, hiệu quả và độ tin cậy. Các lượt xử lý này sẽ cho phép bạn áp dụng các cài đặt mã hoá và nén cho toàn bộ thư mục hình ảnh cùng một lúc, mà không cần bạn cần mở phần mềm chỉnh sửa ảnh, và theo cách giữ lại các nguồn ảnh gốc, những cài đặt đó cần được điều chỉnh một cách nhanh chóng. Quảng cáo được thiết kế để chạy trong nhiều bối cảnh, từ môi trường phát triển tại địa phương cho đến máy chủ web – ví dụ: ImageMin tập trung vào nén để Node.js có thể được mở rộng để phù hợp với các ứng dụng cụ thể thông qua một loạt trình bổ trợ, trong khi ImageMagickSharp dựa trên Node.js đến với vô số tính năng ngay từ đầu.

Các thư viện xử lý hình ảnh này giúp nhà phát triển xây dựng các công cụ dành riêng cho việc tối ưu hoá hình ảnh một cách liền mạch trong quá trình phát triển chuẩn, tức là đảm bảo dự án của bạn luôn tham chiếu đến hình ảnh sẵn sàng cho việc sản xuất với chi phí thấp nhất có thể.

Công cụ và quy trình công việc cho nhà phát triển cục bộ

Trình chạy tác vụ và trình gói như Grunt, Gulp hoặc Webpack có thể được sử dụng để tối ưu hoá thành phần hình ảnh cùng với các công việc phổ biến khác liên quan đến hiệu suất, chẳng hạn như giảm thiểu CSS và JavaScript. Người nhận hãy minh hoạ, hãy xem một trường hợp sử dụng tương đối đơn giản: một thư mục trong dự án của bạn chứa hàng chục hình ảnh, được dùng trên một trang web công khai.

Trước tiên, bạn cần đảm bảo phương thức mã hoá nhất quán và hiệu quả cho những hình ảnh này. Như bạn đã tìm hiểu trong các mô-đun trước, WebP là một định dạng mặc định hiệu quả cho hình ảnh về cả chất lượng và kích thước tệp. WebP được hỗ trợ tốt, nhưng không được hỗ trợ chung. Vì vậy, bạn cũng cần thêm một bản sao dự phòng ở dạng JPEG tăng dần. Sau đó: để sử dụng thuộc tính srcset nhằm phân phối những thành phần này một cách hiệu quả, bạn sẽ cần tạo nhiều kích thước thay thế cho mỗi kiểu mã hoá.

Mặc dù việc này sẽ là một công việc lặp đi lặp lại và tốn thời gian nếu thực hiện bằng phần mềm chỉnh sửa hình ảnh, nhưng những người chạy nhiệm vụ thích Gulp được thiết kế để tự động hoá chính xác việc lặp lại này. Hàm gulp-đáp ứng Trình bổ trợ sử dụng Sharp là một trong nhiều trình bổ trợ tuân theo một mẫu tương tự: thu thập tất cả tệp trong thư mục nguồn, mã hoá lại và nén chúng dựa trên cùng một "chất lượng" chuẩn cách viết tắt mà bạn đã tìm hiểu trong phần Định dạng và nén hình ảnh. Sau đó, các tệp kết quả được xuất sang đường dẫn mà bạn xác định, sẵn sàng để tham chiếu trong thuộc tính src của các phần tử img mà người dùng nhìn thấy, đồng thời giữ nguyên tệp gốc.

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/'));
}

Với quy trình như vậy, sẽ không có tác hại nào đến môi trường sản xuất nếu có ai đó trong dự án vô tình đã thêm một ảnh được mã hoá dưới dạng PNG màu thực lớn vào thư mục chứa nguồn ảnh gốc của bạn—bất kể mã hoá hình ảnh gốc, tác vụ này sẽ tạo ra một WebP hiệu quả và dự phòng JPEG tiến bộ đáng tin cậy, đồng thời ở mức nén có thể dễ dàng điều chỉnh khi di chuyển. Tất nhiên, quy trình này cũng đảm bảo rằng hình ảnh gốc của bạn các tệp này sẽ được lưu trong môi trường phát triển của dự án, nghĩa là bạn có thể điều chỉnh các chế độ cài đặt này bất cứ lúc nào khi chỉ có đầu ra tự động được ghi đè.

Để xuất nhiều tệp, bạn truyền nhiều đối tượng cấu hình giống nhau, ngoài việc thêm các đối tượng cấu hình khoá width và một giá trị tính bằng pixel:

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/'));
}

Trong trường hợp của ví dụ trên, hình ảnh gốc (monarch.png) lớn hơn 3,3 MB. Tệp lớn nhất được tạo bởi nhiệm vụ này (monarch-1000.jpeg) xấp xỉ 150KB. Tệp nhỏ nhất, monarch-400.web, chỉ có 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

Tất nhiên, bạn sẽ muốn kiểm tra kỹ kết quả để tìm các cấu phần phần mềm nén dễ thấy hoặc có thể tăng độ nén để tiết kiệm thêm. Vì tác vụ này không phá hủy, nên bạn có thể dễ dàng thay đổi các cài đặt này.

Nói chung, bạn sẽ có được một quy trình tối ưu hoá thủ công để đổi lấy số ít kilobyte không chỉ hiệu quả mà còn linh hoạt – một công cụ áp dụng liền mạch kiến thức của bạn về thành phần hình ảnh có hiệu suất cao cho toàn bộ dự án mà không có bất kỳ sự can thiệp thủ công nào.

Áp dụng mã đánh dấu hình ảnh thích ứng

Việc điền sẵn các thuộc tính srcset thường là một quy trình thủ công đơn giản, vì thuộc tính này thực sự chỉ thu thập thông tin về cấu hình mà bạn đã thực hiện khi tạo nguồn. Trong các nhiệm vụ trên, chúng tôi đã thiết lập tên tệp và thông tin về chiều rộng mà thuộc tính của chúng ta sẽ tuân theo:

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

Hãy nhớ rằng nội dung của thuộc tính srcset chỉ mang tính mô tả chứ không phải là nội dung quy định. Việc nạp chồng tệp không có hại gì srcset, miễn là tỷ lệ khung hình của mọi nguồn đều nhất quán. Thuộc tính srcset có thể chứa URI và chiều rộng của mọi thao tác cắt thay thế do máy chủ tạo ra mà không gây ra yêu cầu không cần thiết nào và mà chúng tôi cung cấp cho hình ảnh hiển thị, thì trình duyệt càng có thể điều chỉnh yêu cầu hiệu quả hơn.

Như đã tìm hiểu trong Hình ảnh thích ứng, bạn sẽ muốn sử dụng phần tử <picture> để xử lý WebP liền mạch hoặc mẫu dự phòng JPEG. Trong trường hợp này, bạn sẽ sử dụng thuộc tính type cùng với srcset.

<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>

Như bạn đã tìm hiểu, các trình duyệt hỗ trợ WebP sẽ nhận dạng nội dung của thuộc tính type và chọn <source> đó thuộc tính srcset của phần tử làm danh sách các đề xuất hình ảnh. Trình duyệt không nhận dạng image/webp là nội dung nghe nhìn hợp lệ sẽ bỏ qua <source> này và thay vào đó sẽ sử dụng thuộc tính srcset của phần tử <img> bên trong.

Có một điểm khác cần cân nhắc về việc hỗ trợ trình duyệt: những trình duyệt không hỗ trợ mã đánh dấu hình ảnh thích ứng sẽ vẫn cần một hình ảnh dự phòng, nếu không chúng ta có thể gặp rủi ro hình ảnh bị hỏng trong ngữ cảnh duyệt web đặc biệt cũ. Vì <picture>, <source>srcset đều bị bỏ qua trong các trình duyệt này, nên chúng ta sẽ muốn chỉ định một nguồn mặc định trong <img> bên trong src.

Vì việc điều chỉnh tỷ lệ hình ảnh xuống dưới là hình ảnh liền mạch trực quan và phương thức mã hoá JPEG được hỗ trợ trên toàn cầu, nên JPEG lớn nhất là đó là một lựa chọn hợp lý.

<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 có thể khó xử lý hơn một chút. Như bạn đã tìm hiểu, sizes nhất thiết phải theo ngữ cảnh — bạn không thể điền thuộc tính nếu không biết lượng không gian mà hình ảnh sẽ chiếm trong bố cục kết xuất. Cho các yêu cầu hiệu quả nhất có thể, thì bạn cần cung cấp thuộc tính sizes chính xác trong mã đánh dấu tại thời điểm các yêu cầu đó do người dùng cuối tạo, từ rất lâu trước khi người dùng yêu cầu các kiểu chi phối bố cục trang. Bỏ qua hoàn toàn sizes không chỉ vi phạm về quy cách HTML mà còn dẫn đến hành vi mặc định tương đương với sizes="100vw"—thông báo trình duyệt mà hình ảnh này chỉ bị hạn chế bởi chính khung nhìn, dẫn đến nguồn ứng viên lớn nhất có thể đang được chọn.

Tương tự như với bất kỳ nhiệm vụ phát triển web đặc biệt nặng nề nào, một số công cụ đã được tạo ra để giúp loại bỏ quá trình viết tay các thuộc tính sizes. respImageLint hoàn toàn là một đoạn mã thiết yếu dùng để kiểm tra độ chính xác của các thuộc tính sizes và đưa ra đề xuất cải thiện. Công cụ này chạy dưới dạng một dấu trang—một công cụ bạn chạy trong trình duyệt của mình trong khi trỏ đến trang được hiển thị đầy đủ có chứa hình ảnh của bạn phần tử. Trong bối cảnh mà trình duyệt hiểu rõ về bố cục trang, trình duyệt cũng sẽ có độ phân giải gần như hoàn hảo khả năng nhận biết không gian mà hình ảnh cần chiếm trong bố cục đó ở mọi kích thước khung nhìn có thể có.

Báo cáo hình ảnh thích ứng cho thấy kích thước/chiều rộng không khớp.

Công cụ tìm lỗi mã nguồn các thuộc tính sizes chắc chắn sẽ hữu ích, nhưng công cụ này thậm chí còn có giá trị hơn nữa trong vai trò một công cụ giúp bạn bán buôn các thuộc tính đó. Như bạn đã biết, cú pháp srcsetsizes dùng để tối ưu hoá các yêu cầu cho thành phần hình ảnh theo cách liền mạch về mặt hình ảnh. Mặc dù không phải là thứ nên dùng trong phiên bản chính thức, giá trị phần giữ chỗ sizes mặc định của 100vw là hoàn toàn hợp lý khi xem xét bố cục của trang trong môi trường phát triển tại địa phương. Sau khi có kiểu bố cục, chạy respImageLint sẽ cung cấp cho bạn các thuộc tính sizes phù hợp mà bạn có thể sao chép và dán vào mã đánh dấu của mình với mức độ chi tiết cao hơn nhiều so với một bài viết được viết bằng tay:

Báo cáo hình ảnh thích ứng có phương diện được đề xuất.

Mặc dù các yêu cầu hình ảnh do mã đánh dấu do máy chủ thực hiện xảy ra quá nhanh đối với JavaScript để tạo thuộc tính sizes phía máy khách, lý do tương tự sẽ không được áp dụng nếu những yêu cầu đó được bắt đầu từ phía máy khách. Dự án Lazysizes, ví dụ: cho phép bạn trì hoãn hoàn toàn các yêu cầu hình ảnh cho đến khi bố cục được thiết lập, cho phép JavaScript tạo Giá trị sizes dành cho chúng tôi – điều này mang lại sự thuận tiện rất lớn cho bạn và đảm bảo đáp ứng các yêu cầu hiệu quả nhất có thể cho người dùng của bạn. Tuy nhiên, hãy nhớ rằng phương pháp này đồng nghĩa với việc hy sinh độ tin cậy của mã đánh dấu được kết xuất trên máy chủ và tốc độ tối ưu hoá được tích hợp vào trình duyệt và việc khởi tạo các yêu cầu này chỉ sau khi trang đã hiển thị sẽ có kích thước lớn tác động tiêu cực đến điểm LCP của bạn.

Tất nhiên, nếu bạn đã phụ thuộc vào một khung hiển thị phía máy khách như React hoặc Vue, thì bạn cũng sẽ có một khoản nợ và trong những trường hợp như vậy, việc sử dụng Lazysizes có nghĩa là các thuộc tính sizes của bạn có thể gần như bị loại bỏ hoàn toàn. Vẫn tốt hơn: vì sizes="auto" trên các hình ảnh tải từng phần đã nhận được sự đồng thuận và các triển khai gốc, Lazysizes sẽ trở thành một polyfill cho hành vi mới được chuẩn hoá đó của trình duyệt.