Tự động lựa chọn tài nguyên thông qua gợi ý của ứng dụng

Ilya Grigorik
Ilya Grigorik

Việc xây dựng cho web mang đến cho bạn phạm vi tiếp cận chưa từng có. Bạn chỉ cần nhấp chuột là có thể sử dụng ứng dụng web của mình trên hầu hết mọi thiết bị thông minh, máy tính bảng, máy tính xách tay, máy tính để bàn, TV và nhiều thiết bị khác, bất kể thương hiệu hay nền tảng. Để mang lại trải nghiệm tốt nhất, bạn đã tạo một trang web thích ứng có khả năng điều chỉnh cách trình bày và chức năng cho từng hệ số hình dạng. Giờ đây, bạn đang chạy danh sách kiểm tra hiệu suất để đảm bảo ứng dụng tải nhanh nhất có thể: bạn đã tối ưu hoá đường dẫn kết xuất quan trọng, đã nén và lưu vào bộ nhớ đệm tài nguyên văn bản. Bây giờ, bạn đang xem phần lớn các byte trong tài khoản để chuyển dữ liệu. Vấn đề là việc tối ưu hoá hình ảnh khó:

  • Xác định định dạng phù hợp (vectơ so với đường quét)
  • Xác định các định dạng mã hoá tối ưu (jpeg, webp, v.v.)
  • Xác định chế độ nén phù hợp (giảm và không tổn hao)
  • Xác định siêu dữ liệu cần giữ lại hoặc loại bỏ
  • Tạo nhiều biến thể của mỗi biến thể cho từng màn hình + độ phân giải DPR
  • ...
  • Tài khoản cho loại mạng, tốc độ và lựa chọn ưu tiên của người dùng

Nói chung, đây là những vấn đề mà mọi người đều hiểu rõ. Nói chung, chúng tạo ra một không gian tối ưu hoá lớn mà chúng tôi (các nhà phát triển) thường bỏ qua hoặc bỏ qua. Con người chỉ khám phá lặp đi lặp lại cùng một không gian tìm kiếm kém chất lượng, đặc biệt là khi có nhiều bước. Mặt khác, máy tính làm tốt hơn các loại tác vụ này.

Câu trả lời cho một chiến lược tối ưu hoá hiệu quả và bền vững cho hình ảnh cũng như các tài nguyên khác có thuộc tính tương tự thật đơn giản, đó là: tự động hoá. Nếu bạn tự điều chỉnh tài nguyên của mình, bạn sẽ làm sai: bạn sẽ quên, bạn sẽ trở nên lười biếng, hoặc người khác sẽ mắc lỗi này thay cho bạn.

Câu chuyện về nhà phát triển chú trọng vào hiệu suất

Việc tìm kiếm thông qua không gian tối ưu hoá hình ảnh có hai giai đoạn riêng biệt: thời gian xây dựng và thời gian chạy.

  • Một số biện pháp tối ưu hoá là hàm nội tại của chính tài nguyên – ví dụ: chọn định dạng và loại mã hoá phù hợp, điều chỉnh chế độ cài đặt nén cho từng bộ mã hoá, loại bỏ siêu dữ liệu không cần thiết, v.v. Bạn có thể thực hiện các bước này tại "thời gian xây dựng".
  • Các hoạt động tối ưu hoá khác được xác định theo loại và thuộc tính mà ứng dụng yêu cầu và phải được thực hiện tại "thời gian chạy": chọn tài nguyên phù hợp cho DPR của ứng dụng và chiều rộng hiển thị dự kiến, có xét đến tốc độ mạng của ứng dụng, các lựa chọn ưu tiên về người dùng và ứng dụng, v.v.

Công cụ thời gian xây dựng đã tồn tại nhưng vẫn cần được cải thiện. Ví dụ: bạn có thể tiết kiệm được rất nhiều bằng cách tự động điều chỉnh chế độ cài đặt "chất lượng" cho mỗi hình ảnh và từng định dạng hình ảnh, nhưng tôi chưa thấy có ai thực sự sử dụng chế độ này ngoài việc nghiên cứu. Đây là lĩnh vực đầy ắp sự đổi mới, nhưng với mục đích của bài đăng này, tôi sẽ giữ nguyên phần đó. Hãy tập trung vào phần thời gian của câu chuyện.

<img src="/image/thing" sizes="50vw"
        alt="image thing displayed at 50% of viewport width">

Mục đích của ứng dụng rất đơn giản: tìm nạp và hiển thị hình ảnh ở 50% khung nhìn của người dùng. Đây là nơi hầu hết mọi nhà thiết kế đều rửa tay và đầu khi ở quầy bar. Trong khi đó, nhà phát triển quan tâm đến hiệu suất trong nhóm đã trải qua một đêm dài:

  1. Để có được định dạng nén tốt nhất, cô ấy muốn sử dụng định dạng hình ảnh tối ưu cho từng ứng dụng: WebP cho Chrome, JPEG XR cho Edge và JPEG cho các ứng dụng còn lại.
  2. Để có được chất lượng hình ảnh tốt nhất, cô ấy cần tạo nhiều biến thể của từng hình ảnh ở các độ phân giải khác nhau: 1x, 1,5x, 2x, 2,5x, 3x và thậm chí có thể nhiều hơn nữa.
  3. Để tránh phân phối các pixel không cần thiết, cô cần hiểu được ý nghĩa thực sự của "50% khung nhìn của người dùng" – có rất nhiều chiều rộng khung nhìn khác nhau!
  4. Lý tưởng nhất là cô ấy cũng muốn mang đến trải nghiệm linh hoạt, trong đó người dùng trên các mạng chậm hơn sẽ tự động tìm nạp độ phân giải thấp hơn. Vậy là bạn đã đến lúc uống kính rồi.
  5. Ứng dụng cũng hiển thị một số chế độ kiểm soát của người dùng ảnh hưởng đến tài nguyên hình ảnh nào cần được tìm nạp, vì vậy cũng cần có yếu tố đó.

Sau đó, nhà thiết kế nhận ra rằng cô ấy cần hiển thị một hình ảnh khác có chiều rộng 100% nếu kích thước khung nhìn nhỏ để tối ưu hoá mức độ dễ đọc. Điều này có nghĩa là chúng ta hiện phải lặp lại cùng một quy trình cho một nội dung khác, sau đó đặt điều kiện tìm nạp đối với kích thước khung nhìn. Tôi đã từng đề cập rằng vấn đề này khó chưa? Chà, hãy bắt đầu thôi. Phần tử picture sẽ giúp chúng ta khá xa:

<picture>
    <!-- serve WebP to Chrome and Opera -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w,
        /image/thing-800.webp 800w, /image/thing-1200.webp 1200w,
        /image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w"
    type="image/webp">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w,
        /image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w,
        /image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w"
    type="image/webp">
    <!-- serve JPEGXR to Edge -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w,
        /image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w,
        /image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr 400w,
        /image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr 1200w,
        /image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr 2000w"
    type="image/vnd.ms-photo">
    <!-- serve JPEG to others -->
    <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
        /image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
        /image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w">
    <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w,
        /image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w,
        /image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w">
    <!-- fallback for browsers that don't support picture -->
    <img src="/image/thing.jpg" width="50%">
</picture>

Chúng tôi đã xử lý hướng nghệ thuật, lựa chọn định dạng và cung cấp 6 biến thể của mỗi hình ảnh để tính đến sự thay đổi về DPR và chiều rộng khung nhìn của thiết bị của khách hàng. Thật ấn tượng!

Rất tiếc, phần tử picture không cho phép chúng ta xác định bất kỳ quy tắc nào về cách hoạt động dựa trên loại kết nối hoặc tốc độ của ứng dụng. Điều đó có nghĩa là thuật toán xử lý của lớp này cho phép tác nhân người dùng điều chỉnh tài nguyên mà nó tìm nạp trong một số trường hợp – hãy xem bước 5. Chúng ta chỉ phải hy vọng rằng tác nhân người dùng đó đủ thông minh. (Lưu ý: hiện không có cách triển khai nào). Tương tự, không có hook trong phần tử picture để cho phép logic dành riêng cho ứng dụng có tính đến các lựa chọn ưu tiên của người dùng hoặc ứng dụng. Để có được hai bit cuối cùng này, chúng ta phải chuyển tất cả logic ở trên vào JavaScript, nhưng điều đó sẽ mất tính năng tối ưu hoá tải trước trình quét do picture cung cấp. Rất tiếc!

Bỏ qua những hạn chế đó, cách này hiệu quả. Ít nhất là đối với tài sản cụ thể này. Thách thức thực sự và lâu dài ở đây là chúng tôi không thể kỳ vọng nhà thiết kế hoặc nhà phát triển có thể chế tạo mã bằng tay như thế này cho từng tài sản. Đây là một câu đố trí tuệ thú vị ở lần thử đầu tiên, nhưng nó sẽ mất đi sự hấp dẫn ngay sau đó. Chúng ta cần hệ thống tự động hoá. Có thể công cụ của IDE hoặc công cụ biến đổi nội dung khác có thể giúp chúng ta tiết kiệm và tự động tạo bản mẫu ở trên.

Tự động chọn tài nguyên bằng gợi ý ứng dụng

Hãy hít một hơi thật sâu, tạm ngưng việc bạn không tin và bây giờ, hãy xem xét ví dụ sau:

<meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width">
...
<picture>
    <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing">
    <img sizes="100vw" src="/image/thing-crop">
</picture>

Dù bạn có tin hay không, ví dụ trên cũng đủ cung cấp tất cả các tính năng tương tự như mã đánh dấu hình ảnh dài hơn nhiều ở trên, ngoài ra, như chúng ta sẽ thấy, nó cho phép nhà phát triển có toàn quyền kiểm soát đối với cách tìm nạp tài nguyên hình ảnh và tài nguyên hình ảnh. "Điều kỳ diệu" nằm trong dòng đầu tiên bật tính năng báo cáo gợi ý ứng dụng và yêu cầu trình duyệt quảng cáo tỷ lệ pixel của thiết bị (DPR), chiều rộng khung nhìn bố cục (Viewport-Width) và chiều rộng dự kiến hiển thị (Width) của tài nguyên cho máy chủ.

Khi bật gợi ý ứng dụng, thẻ đánh dấu phía máy khách thu được sẽ chỉ giữ lại các yêu cầu về cách trình bày. Nhà thiết kế không phải lo lắng về loại hình ảnh, độ phân giải ứng dụng, các điểm ngắt tối ưu để giảm số byte được phân phối hoặc các tiêu chí lựa chọn tài nguyên khác. Hãy đối mặt với vấn đề này, họ không bao giờ làm như vậy và họ không cần phải làm như vậy. Tốt hơn là nhà phát triển cũng không cần viết lại và mở rộng mã đánh dấu ở trên vì việc lựa chọn tài nguyên thực tế là do máy khách và máy chủ thương lượng.

Chrome 46 cung cấp dịch vụ hỗ trợ gốc cho các gợi ý DPR, WidthViewport-Width. Theo mặc định, các gợi ý sẽ bị tắt và <meta http-equiv="Accept-CH" content="..."> ở trên đóng vai trò là tín hiệu chọn tham gia, cho Chrome biết phải thêm các tiêu đề đã chỉ định vào các yêu cầu gửi đi. Với vị trí đó, hãy kiểm tra tiêu đề yêu cầu và tiêu đề phản hồi để tìm yêu cầu hình ảnh mẫu:

Sơ đồ thương lượng gợi ý ứng dụng

Chrome quảng cáo khả năng hỗ trợ định dạng WebP thông qua tiêu đề của yêu cầu Accept (Chấp nhận); trình duyệt Edge mới cũng quảng cáo khả năng hỗ trợ JPEG XR thông qua tiêu đề Accept (Chấp nhận).

Ba tiêu đề yêu cầu tiếp theo là các tiêu đề gợi ý ứng dụng quảng cáo tỷ lệ pixel của thiết bị ứng dụng (3x), chiều rộng khung nhìn bố cục (460px) và chiều rộng dự kiến hiển thị của tài nguyên (230px). Việc này sẽ cung cấp tất cả thông tin cần thiết cho máy chủ để chọn biến thể hình ảnh tối ưu dựa trên bộ chính sách riêng: khả năng sử dụng tài nguyên được tạo trước, chi phí mã hoá lại hoặc đổi kích thước tài nguyên, mức độ phổ biến của tài nguyên, tải máy chủ hiện tại, v.v. Trong trường hợp cụ thể này, máy chủ sẽ sử dụng các gợi ý DPRWidth, đồng thời trả về một tài nguyên WebP, như được biểu thị bằng các tiêu đề Content-Type, Content-DPRVary.

Không có phép màu nào ở đây. Chúng tôi đã chuyển phần lựa chọn tài nguyên từ mã đánh dấu HTML và vào quy trình thương lượng yêu cầu-phản hồi giữa ứng dụng và máy chủ. Do đó, HTML chỉ quan tâm đến các yêu cầu về trình bày và là điều chúng tôi có thể tin tưởng là bất kỳ nhà thiết kế và nhà phát triển nào có thể viết, trong khi việc tìm kiếm thông qua không gian tối ưu hoá hình ảnh được trì hoãn đến máy tính và hiện có thể dễ dàng tự động hoá trên quy mô lớn. Bạn còn nhớ nhà phát triển chú trọng vào hiệu suất của chúng tôi không? Công việc của cô bây giờ là viết một dịch vụ hình ảnh có thể tận dụng các gợi ý được cung cấp và trả về phản hồi phù hợp: cô có thể sử dụng bất kỳ ngôn ngữ hoặc máy chủ nào mình thích, hoặc để dịch vụ bên thứ ba hoặc CDN thay mặt cô thực hiện việc này.

<img src="/image/thing" sizes="50vw"
        alt="image thing displayed at 50% of viewport width">

Ngoài ra, bạn còn nhớ anh chàng này ở trên không? Với gợi ý của máy khách, thẻ hình ảnh đơn giản giờ đây là DPR, nhận biết khung nhìn và nhận biết chiều rộng mà không có thêm bất kỳ thẻ đánh dấu nào khác. Nếu cần thêm hướng nghệ thuật, bạn có thể sử dụng thẻ picture, như chúng tôi đã minh hoạ ở trên, nếu không thì tất cả các thẻ hình ảnh hiện có của bạn sẽ trở nên thông minh hơn rất nhiều. Tính năng gợi ý ứng dụng giúp cải thiện các phần tử imgpicture hiện có.

Kiểm soát việc lựa chọn tài nguyên bằng trình chạy dịch vụ

Trên thực tế, ServiceWorker là một proxy phía máy khách đang chạy trong trình duyệt của bạn. API này chặn mọi yêu cầu gửi đi và cho phép bạn kiểm tra, viết lại, lưu vào bộ nhớ đệm và thậm chí là tổng hợp các phản hồi. Hình ảnh cũng không khác nhau và khi bật gợi ý ứng dụng, ServiceWorker đang hoạt động có thể xác định các yêu cầu hình ảnh, kiểm tra gợi ý ứng dụng được cung cấp và xác định logic xử lý riêng.

self.onfetch = function(event) {
    var req = event.request.clone();
    console.log("SW received request for: " + req.url)
    for (var entry of req.headers.entries()) {
    console.log("\t" + entry[0] +": " + entry[1])
    }
    ...
}
Gợi ý ứng dụng serviceWorker.

ServiceWorker cho phép bạn toàn quyền kiểm soát phía máy khách đối với việc lựa chọn tài nguyên. Điều này rất quan trọng. Hãy để điều đó chìm vào vì khả năng gần như vô hạn:

  • Bạn có thể viết lại các giá trị tiêu đề gợi ý ứng dụng do tác nhân người dùng đặt.
  • Bạn có thể bổ sung các giá trị tiêu đề mới của gợi ý ứng dụng vào yêu cầu.
  • Bạn có thể ghi lại URL và trỏ yêu cầu hình ảnh đến một máy chủ thay thế (ví dụ: CDN).
    • Bạn thậm chí có thể di chuyển giá trị gợi ý từ tiêu đề và vào chính URL nếu việc đó giúp việc triển khai trong cơ sở hạ tầng của bạn dễ dàng hơn.
  • Bạn có thể lưu các phản hồi vào bộ nhớ đệm và xác định logic riêng cho những tài nguyên được phân phát.
  • Bạn có thể điều chỉnh câu trả lời của mình dựa trên khả năng kết nối của người dùng.
  • Bạn có thể tính đến các tùy chọn ghi đè ứng dụng và tùy chọn người dùng.
  • Bạn có thể... thực sự làm bất cứ điều gì mà trái tim bạn mong muốn.

Phần tử picture cung cấp chức năng kiểm soát hướng nghệ thuật cần thiết trong mã đánh dấu HTML. Gợi ý ứng dụng cung cấp chú giải về các yêu cầu hình ảnh kết quả cho phép tự động chọn tài nguyên. ServiceWorker cung cấp khả năng quản lý yêu cầu và phản hồi trên ứng dụng. Đây là trang web có thể mở rộng đang hoạt động.

Câu hỏi thường gặp về gợi ý của ứng dụng

  1. Gợi ý ứng dụng hiện có ở đâu? Vận chuyển trong Chrome 46. Đang cân nhắc trong FirefoxEdge.

  2. Tại sao tính năng gợi ý ứng dụng chọn sử dụng? Chúng ta muốn giảm thiểu chi phí cho các trang web không sử dụng gợi ý ứng dụng. Để bật gợi ý ứng dụng, trang web nên cung cấp tiêu đề Accept-CH hoặc lệnh <meta http-equiv> tương đương trong mã đánh dấu trang. Khi có một trong hai thuộc tính đó, tác nhân người dùng sẽ thêm các gợi ý thích hợp vào tất cả yêu cầu về tài nguyên phụ. Trong tương lai, chúng tôi có thể cung cấp một cơ chế bổ sung để duy trì lựa chọn ưu tiên này cho một nguồn gốc cụ thể, cho phép các gợi ý tương tự được phân phối trong các yêu cầu chỉ đường.

  3. Tại sao chúng ta cần thông tin mô tả của ứng dụng nếu có ServiceWorker? ServiceWorker không có quyền truy cập vào thông tin về bố cục, tài nguyên và chiều rộng khung nhìn. Ít nhất, nếu không có các lượt trọn vòng tốn kém và trì hoãn đáng kể yêu cầu hình ảnh, ví dụ: khi trình phân tích cú pháp tải trước khởi tạo yêu cầu hình ảnh. Gợi ý ứng dụng tích hợp với trình duyệt để cung cấp dữ liệu này theo yêu cầu.

  4. Gợi ý ứng dụng có phải chỉ dành cho tài nguyên hình ảnh không? Trường hợp sử dụng chính đằng sau các gợi ý về DPR, Viewport-Width và Width là để bật tính năng lựa chọn tài nguyên cho các thành phần hình ảnh. Tuy nhiên, cùng một gợi ý được phân phối cho mọi tài nguyên phụ bất kể là loại nào – ví dụ: các yêu cầu CSS và JavaScript cũng nhận được cùng thông tin và có thể dùng để tối ưu hoá các tài nguyên đó.

  5. Tại sao một số yêu cầu hình ảnh không báo cáo Chiều rộng? Trình duyệt có thể không biết chiều rộng màn hình dự kiến vì trang web dựa vào kích thước nội tại của hình ảnh. Do đó, gợi ý chiều rộng sẽ bị bỏ qua đối với các yêu cầu như vậy và với các yêu cầu không có "chiều rộng hiển thị" (ví dụ: tài nguyên JavaScript). Để nhận gợi ý về Chiều rộng, hãy nhớ chỉ định một giá trị kích thước cho hình ảnh của bạn.

  6. Vậy còn <insert my favorite hint> thì sao? ServiceWorker cho phép nhà phát triển chặn và sửa đổi (ví dụ: thêm tiêu đề mới) tất cả các yêu cầu gửi đi. Ví dụ: bạn có thể dễ dàng thêm thông tin dựa trên NetInfo để cho biết loại kết nối hiện tại -- xem phần "Báo cáo về chức năng bằng ServiceWorker". Gợi ý "gốc" được vận chuyển trong Chrome (DPR, Chiều rộng, Chiều rộng tài nguyên) được triển khai trong trình duyệt vì việc triển khai dựa trên SW thuần túy sẽ trì hoãn tất cả các yêu cầu hình ảnh.

  7. Tôi có thể tìm hiểu thêm, xem thêm bản minh hoạ ở đâu? Hãy xem tài liệu giải thíchmở một vấn đề trên GitHub nếu bạn có ý kiến phản hồi hoặc thắc mắc khác.