Cách đây hơn một năm, nhóm Chrome Aurora đã ra mắt chỉ thị NgOptimizedImage của Angular. Chỉ thị này chủ yếu tập trung vào việc cải thiện hiệu suất thông qua các chỉ số Core Web Vitals. API này kết hợp các phương pháp tối ưu hoá phổ biến và các phương pháp hay nhất cho hình ảnh vào một API dành cho người dùng mà không phức tạp hơn nhiều so với phần tử <img>
tiêu chuẩn.
Năm 2023, chúng tôi đã cải tiến directive này bằng các tính năng mới. Bài đăng này mô tả quan trọng nhất trong số các tính năng mới đó, đồng thời nhấn mạnh lý do chúng tôi chọn ưu tiên mỗi tính năng và cách tính năng này có thể giúp cải thiện hiệu suất của các ứng dụng Angular.
Tính năng mới
NgOptimizedImage đã cải thiện đáng kể theo thời gian, bao gồm cả các tính năng mới sau.
Chế độ tô màu nền
Việc định cỡ hình ảnh bằng cách cung cấp thuộc tính width
và height
là một hoạt động tối ưu hoá cực kỳ quan trọng để giảm sự dịch chuyển bố cục, vì trình duyệt cần biết tỷ lệ khung hình của hình ảnh để tiết kiệm dung lượng cho hình ảnh đó. Tuy nhiên, việc định cỡ hình ảnh là công việc bổ sung cho nhà phát triển ứng dụng và không phù hợp với một số trường hợp sử dụng hình ảnh.
Giúp giải quyết sự căng thẳng này là tính năng chính đầu tiên được thêm vào thành phần hình ảnh sau bản xem trước dành cho nhà phát triển: chế độ tô. Đây là cách để nhà phát triển đưa hình ảnh vào mà không cần xác định rõ kích thước và không gây ra sự thay đổi bố cục.
Với chế độ lấp đầy, yêu cầu về kích thước hình ảnh sẽ bị tắt và hình ảnh sẽ tự động được tạo kiểu để lấp đầy phần tử chứa hình ảnh đó. Điều này tách tỷ lệ khung hình của hình ảnh khỏi không gian mà hình ảnh đó chiếm trên trang, đồng thời giúp bạn kiểm soát tốt hơn cách hình ảnh phù hợp với bố cục trang.
Chế độ lấp đầy sử dụng NgOptimizedImage làm giải pháp thay thế hiệu quả hơn cho thuộc tính css background-image
. Đặt hình ảnh bên trong <div>
hoặc phần tử khác có kiểu background-image
, sau đó bật chế độ tô, như minh hoạ trong ví dụ về mã trước. Sử dụng các thuộc tính CSS object-fit
và object-position
trên <div>
để kiểm soát cách đặt hình ảnh ở chế độ nền.
// Height and width are required
<img ngSrc="example.com" height="300" width="400">
// Unless you use fill mode!
<div style="width: 100vw; height: 50em; position: relative">
<img ngSrc="example.com" fill>
</div>
Tạo srcset
Một trong những kỹ thuật tối ưu hoá hình ảnh hiệu quả nhất là sử dụng thuộc tính srcset
để đảm bảo tải hình ảnh có kích thước phù hợp xuống cho mọi thiết bị truy cập vào ứng dụng của bạn. Việc sử dụng srcset
trong toàn bộ ứng dụng có thể giúp bạn tránh lãng phí băng thông và cải thiện đáng kể LCP Core Web Vital.
Nhược điểm của thuộc tính srcset
là có thể khó triển khai. Việc viết thủ công các giá trị srcset
có nghĩa là thêm nhiều dòng mã đánh dấu vào mỗi phần tử hình ảnh trong ứng dụng, cùng với nhiều URL tuỳ chỉnh cho mỗi srcset
. Bạn cũng phải quyết định một tập hợp các điểm ngắt. Việc này khá phức tạp vì các điểm ngắt có thể đại diện cho cả mật độ màn hình và kích thước khung nhìn của các thiết bị phổ biến.
Đó là lý do việc thêm tính năng tạo srcset tự động vào lệnh NgOptimizedImage là một mốc quan trọng sau khi ra mắt. Với tính năng bổ sung này, mọi ứng dụng sử dụng CDN hỗ trợ đổi kích thước hình ảnh đều có thể tự động thêm các nhóm tài nguyên srcset đầy đủ, có thể tuỳ chỉnh vào mọi hình ảnh được tạo bằng lệnh NgOptimizedImage.
Chúng tôi đã đưa vào một API đơn giản để thiết lập thuộc tính sizes
. Thuộc tính này được dùng để đảm bảo rằng mỗi hình ảnh đều có đúng loại srcset
. Nếu bạn không thêm thuộc tính sizes
, chúng ta sẽ biết rằng hình ảnh dành cho kích thước cố định và sẽ nhận được một thuộc tính srcset phụ thuộc vào mật độ, như sau:
<img src="www.example.com/image.png" srcset="www.example.com/image.png?w=400 1x, www.example.com/image.png?w=800 2x" >
Loại srcset này đảm bảo hình ảnh được phân phát ở kích thước có tính đến mật độ pixel trên thiết bị của người dùng.
Mặt khác, nếu bạn thêm thuộc tính sizes
, NgOptimizedImage
sẽ tạo một srcset thích ứng bao gồm các điểm ngắt cho nhiều kích thước hình ảnh và thiết bị phổ biến, bằng cách sử dụng danh sách điểm ngắt mặc định sau:
[16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840]
Tạo kết nối trước
Để cải thiện LCP, điều quan trọng là bạn phải giảm thời gian người dùng tải hình ảnh LCP xuống. Trong phần trước, bạn đã biết cách srcset
có thể hỗ trợ việc chuyển các tệp hình ảnh nhỏ hơn. Tuy nhiên, một điểm tối ưu hoá quan trọng không kém là bắt đầu chuyển càng sớm càng tốt. Một cách để làm điều đó là sử dụng thẻ link rel="preconnect"
để bắt đầu kết nối với miền hình ảnh của bạn.
Ngay từ đầu, NgOptimizedImage cảnh báo nếu bạn không kết nối trước được với miền của hình ảnh LCP, nhưng cảnh báo không phải là giải pháp lý tưởng – chúng tôi chỉ muốn khắc phục vấn đề cho bạn. Và đó chính xác là những gì NgOptimizedImage hiện đang làm, với tính năng tạo kết nối trước tự động.
Để hỗ trợ tính năng này, chúng tôi sử dụng tính năng phân tích mã tĩnh để cố gắng phát hiện các miền hình ảnh trong trình tải NgOptimizedImage và tự động tạo thẻ đường liên kết kết nối trước cho các miền đó. Vẫn có thể có trường hợp cần phải liên kết trước theo cách thủ công, nhưng đối với hầu hết người dùng, tính năng tự động liên kết trước có nghĩa là bạn sẽ không cần thực hiện thêm một bước nào để có được hiệu suất hình ảnh tốt.
Hỗ trợ nâng cao cho trình tải tuỳ chỉnh
Một thành phần chính của NgOptimizedImage là cấu trúc trình tải, cho phép lệnh tự động tạo URL phù hợp với CDN hình ảnh của ứng dụng. Một bộ trình tải tích hợp sẵn được cung cấp cho các CDN được sử dụng rộng rãi. Chúng tôi cũng hỗ trợ bạn sử dụng trình tải tuỳ chỉnh, giúp bạn tích hợp NgOptimizedImage với hầu hết mọi giải pháp lưu trữ hình ảnh.
Khi ra mắt, các trình tải tuỳ chỉnh này chỉ hoạt động trong phạm vi giới hạn và chỉ có thể đọc thuộc tính width
từ phần tử hình ảnh. Để đáp lại ý kiến phản hồi của người dùng, chúng tôi đã hỗ trợ thêm cấu trúc dữ liệu loaderParams
có thể tuỳ chỉnh. Cấu trúc này cho phép truyền dữ liệu tuỳ ý từ phần tử hình ảnh đến trình tải tuỳ chỉnh. Với tiện ích mở rộng này, trình tải tuỳ chỉnh có thể đơn giản hoặc phức tạp tuỳ theo yêu cầu của cơ sở hạ tầng hình ảnh của ứng dụng.
Ví dụ sau đây cho thấy cách một trình tải tuỳ chỉnh đơn giản có thể sử dụng API loaderParams
để chọn giữa hai miền hình ảnh thay thế:
const myCustomLoader = (config: ImageLoaderConfig) => {
if (config.loaderParams?.alternateDomain) {
return `https://alternate.domain.com/images/${config.src}`
}
return `https://primary.domain.com/images/${config.src}`;
};
Bạn có thể xem ví dụ về trình tải tuỳ chỉnh phức tạp hơn trong tài liệu về Angular.
Hướng dẫn mở rộng về hiệu suất hình ảnh
Cho đến nay, mọi cảnh báo hiệu suất hình ảnh mà chúng tôi thêm vào Angular đều nằm trong lệnh NgOptimizedImage. Nếu không sử dụng lệnh này trong ứng dụng, bạn sẽ không nhận được hướng dẫn nào về các vấn đề về hiệu suất hình ảnh.
Trong Angular 17, chúng tôi mở rộng phạm vi hướng dẫn về hiệu suất hình ảnh để bao gồm tất cả ứng dụng Angular. Giờ đây, nếu phát hiện các mẫu hình ảnh mà chúng tôi biết là lỗi gây ảnh hưởng đến hiệu suất, chẳng hạn như tải lười hình ảnh LCP hoặc tải tệp quá lớn cho trang, chúng tôi sẽ thông báo cho bạn, ngay cả khi bạn không sử dụng NgOptimizedImage.
Hiệu suất hình ảnh rất quan trọng đối với tất cả ứng dụng. Chúng tôi rất vui khi tiếp tục xây dựng các hàng rào bảo vệ để giúp ngăn chặn các lỗi thường gặp trong ứng dụng Angular.
Hướng đến tương lai
Chúng tôi đang nỗ lực phát triển bộ tính năng tiếp theo cho NgOptimizedImage. Mặc dù hiệu suất hình ảnh vẫn là mối quan tâm chính của chúng tôi, nhưng chúng tôi cũng muốn thêm các tính năng giúp cải thiện trải nghiệm của nhà phát triển để đảm bảo rằng NgOptimizedImage vẫn là một lựa chọn hấp dẫn để đưa hình ảnh vào các ứng dụng Angular.
Một tính năng mà chúng tôi ưu tiên là phần giữ chỗ hình ảnh. Các thuộc tính này thường được dùng để giúp quá trình tải hình ảnh trông đẹp hơn trên các ứng dụng web, nhưng có thể làm giảm hiệu suất nếu triển khai không đúng cách. Chúng tôi hy vọng xây dựng được một hệ thống giữ chỗ hình ảnh ưu tiên hiệu suất cho NgOptimizedImage. Hãy chú ý theo dõi blog của chúng tôi để biết thêm thông báo!