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 giúp bạn tiếp cận được nhiều người dùng hơn. Ứng dụng web của bạn chỉ cách một lần nhấp và có trên hầu hết mọi thiết bị được kết nối – điện thoại thông minh, máy tính bảng, máy tính xách tay và máy tính để bàn, TV, v.v. – 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, điều chỉnh cách trình bày và chức năng cho từng kiểu dáng. Giờ đây, bạn sẽ xem 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, và giờ đây, bạn sẽ xem xét tài nguyên hình ảnh, thường chiếm phần lớn số byte được chuyển. Vấn đề là tối ưu hoá hình ảnh rất khó:

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

Đây là những vấn đề đã được hiểu rõ. Tổng thể, các yếu tố này tạo ra một không gian tối ưu hoá lớn mà chúng ta (nhà phát triển) thường bỏ qua hoặc xem nhẹ. Con người không thể khám phá cùng một không gian tìm kiếm một cách lặp lại, đặc biệt là khi có nhiều bước liên quan. Mặt khác, máy tính lại rất giỏi trong những loại công việc 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ó các thuộc tính tương tự rất đơn giản: tự động hoá. Nếu bạn đang điều chỉnh tài nguyên theo cách thủ công, thì bạn đang làm sai: bạn sẽ quên, bạn sẽ lười hoặc người khác sẽ mắc lỗi này cho bạn – đảm bảo.

Câu chuyện về nhà phát triển quan tâm đến hiệu suất

Quá trình 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ố hoạt động tối ưu hoá là nội tại của chính tài nguyên – ví dụ: chọn định dạng và loại mã hoá thích 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 tạo bản 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 của ứng dụng yêu cầu hoạt động đó và phải được thực hiện ở "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 màn hình dự kiến, tính đến tốc độ mạng của ứng dụng, lựa chọn ưu tiên của người dùng và ứng dụng, v.v.

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

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

Ý định 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 các nhà thiết kế rửa tay và đi đến quầy bar. Trong khi đó, nhà phát triển quan tâm đến hiệu suất trong nhóm sẽ phải thức đêm:

  1. Để có được mức nén tốt nhất, cô 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ô cần tạo nhiều biến thể của mỗi 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ể thêm một vài biến thể khác.
  3. Để tránh phân phối các pixel không cần thiết, cô cần hiểu "50% khung nhìn của người dùng thực sự có nghĩa là gì" – có rất nhiều chiều rộng khung nhìn khác nhau!
  4. Lý tưởng nhất là cô cũng muốn mang đến một trải nghiệm linh hoạt, trong đó người dùng trên mạng chậm sẽ tự động tìm nạp độ phân giải thấp hơn. Xét cho cùng, đã đến lúc phủ kính.
  5. Ứng dụng cũng hiển thị một số chế độ điều khiển người dùng ảnh hưởng đến tài nguyên hình ảnh cần được tìm nạp, vì vậy, bạn cũng cần tính đến yếu tố đó.

Ồ, và sau đó, nhà thiết kế nhận ra rằng cô cần hiển thị một hình ảnh khác ở chiều rộng 100% nếu kích thước khung nhìn nhỏ để tối ưu hoá khả năng đọc. Điều này có nghĩa là giờ đây, chúng ta phải lặp lại quy trình tương tự cho một thành phần khác, sau đó thực hiện quá trình tìm nạp có điều kiện theo kích thước khung nhìn. Tôi có đề cập đến việc việc này khó không? Được rồi, chúng ta bắt đầu nhé. Phần tử picture sẽ giúp chúng ta đi được một chặng đường dài:

<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ự biến động về DPR và chiều rộng khung nhìn của thiết bị của ứng dụ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 của phần tử đó dựa trên loại hoặc tốc độ kết nối của ứng dụng. Tuy nhiên, trong một số trường hợp, thuật toán xử lý của trình thu thập thông tin người dùng sẽ cho phép điều chỉnh tài nguyên mà trình thu thập thông tin tìm nạp – xem bước 5. Chúng ta chỉ có thể hy vọng rằng tác nhân người dùng đủ thông minh. (Lưu ý: không có phương thức triển khai nào hiện tại). Tương tự, không có lệnh gọi trong phần tử picture để cho phép logic dành riêng cho ứng dụng tính đến các tuỳ chọn của ứng dụng hoặc người dùng. Để có được hai bit cuối cùng này, chúng ta phải di chuyển tất cả logic ở trên vào JavaScript, nhưng điều đó sẽ làm mất các tính năng tối ưu hoá trình quét tải trước do picture cung cấp. Hừm.

Ngoài những hạn chế đó, phương thức này vẫn hoạt động. Í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 ta không thể mong đợi nhà thiết kế hoặc nhà phát triển tạo mã thủ công như thế này cho từng thành phần. Đây là một câu đố vui nhộn trong lần thử đầu tiên, nhưng sẽ mất đi sức hấp dẫn ngay sau đó. Chúng ta cần tự động hoá. Có thể công cụ chuyển đổi nội dung của IDE hoặc công cụ chuyển đổi nội dung khác có thể giúp chúng ta và tự động tạo mã nguyên mẫu ở trên.

Tự động hoá việc 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 thời bỏ qua sự hoài nghi và 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 là đủ để cung cấp tất cả các tính năng giống 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, ví dụ này cho phép nhà phát triển kiểm soát toàn bộ cách thức, tài nguyên hình ảnh nào và thời điểm tìm nạp tài nguyên hình ảnh. "Điều kỳ diệu" nằm ở dòng đầu tiên bật tính năng báo cáo gợi ý của ứ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 hiển thị dự kiến (Width) của tài nguyên cho máy chủ.

Khi bật gợi ý cho ứng dụng, mã đánh dấu phía máy khách thu được sẽ chỉ giữ lại các yêu cầu về bản trình bày. Nhà thiết kế không phải lo lắng về các loại hình ảnh, độ phân giải của ứng dụng, đ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 thực tế rằng họ chưa bao giờ làm như vậy và họ cũng không cần phải làm như vậy. Hơn nữa, nhà phát triển cũng không cần phải viết lại và mở rộng mã đánh dấu ở trên vì lựa chọn tài nguyên thực tế do ứng dụng và máy chủ thương lượng.

Chrome 46 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 để yêu cầu Chrome thêm các tiêu đề đã chỉ định vào các yêu cầu gửi đi. Sau đó, hãy kiểm tra tiêu đề yêu cầu và phản hồi cho một yêu cầu hình ảnh mẫu:

Sơ đồ đàm phán về gợi ý ứng dụng

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

Ba tiêu đề yêu cầu tiếp theo là các tiêu đề gợi ý cho ứng dụng quảng cáo tỷ lệ pixel của thiết bị của ứng dụng (3x), chiều rộng khung nhìn bố cục (460px) và chiều rộng hiển thị dự kiến của tài nguyên (230px). Điều này 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: tình trạng có sẵn của 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ử dụng gợi ý DPRWidth và trả về một tài nguyên WebP, như được chỉ báo 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 lựa chọn tài nguyên từ mã đánh dấu HTML sang quá trình đàm phán yêu cầu-phản hồi giữa ứng dụng và máy chủ. Do đó, HTML chỉ liên quan đến các yêu cầu về bản trình bày và là nội dung mà chúng ta có thể tin tưởng bất kỳ nhà thiết kế và nhà phát triển nào 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 cho 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 quan tâm đến hiệu suất của chúng ta không? Công việc của cô hiện tại 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 thích hợp: cô có thể sử dụng bất kỳ ngôn ngữ hoặc máy chủ nào mà cô thích, hoặc để một 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ó nhớ anh chàng ở trên không? Với các gợi ý của ứng dụng, thẻ hình ảnh đơn giản hiện đã nhận biết được DPR, khung nhìn và chiều rộng mà không cần thêm bất kỳ mã đánh dấu nào. Nếu cần thêm hướng dẫn nghệ thuật, bạn có thể sử dụng thẻ picture như chúng tôi đã minh hoạ ở trên. Ngoài ra, tất 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. Gợi ý cho ứng dụng giúp cải thiện các phần tử imgpicture hiện có.

Kiểm soát lựa chọn tài nguyên bằng worker dịch vụ

Về cơ bản, ServiceWorker là một proxy phía máy khách chạy trong trình duyệt của bạn. Trình chặn này chặn tất cả các yêu cầu gửi đi và cho phép bạn kiểm tra, ghi lại, lưu vào bộ nhớ đệm và thậm chí tổng hợp các phản hồi. Hình ảnh cũng không khác 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ý của riêng mình.

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 lựa chọn tài nguyên ở phía máy khách. Điều này rất quan trọng. Hãy suy ngẫm về điều đó, vì có vô vàn khả năng:

  • Bạn có thể ghi đè 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ể thêm các giá trị tiêu đề gợi ý ứng dụng mới vào yêu cầu.
  • Bạn có thể viết 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 các giá trị gợi ý từ tiêu đề vào chính URL nếu điều đó giúp bạn dễ dàng triển khai trong cơ sở hạ tầng của mình.
  • 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 tài nguyên được phân phát.
  • Bạn có thể điều chỉnh phản hồi 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 tuỳ chọn ghi đè ứng dụng và lựa chọn ưu tiên của người dùng.
  • Bạn có thể … làm bất cứ điều gì bạn 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 ý của ứng dụng cung cấp chú thích về các yêu cầu hình ảnh thu được, cho phép tự động hoá việc lựa chọn tài nguyên. ServiceWorker cung cấp các chức năng quản lý yêu cầu và phản hồi trên ứng dụng. Đây là web có thể mở rộng đang hoạt động.

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

  1. Gợi ý cho ứng dụng có ở đâu? Được phát hành trong Chrome 46. Đang được cân nhắc trong FirefoxEdge.

  2. Tại sao bạn phải chọn sử dụng gợi ý cho ứng dụng? Chúng tôi muốn giảm thiểu hao tổn cho những trang web không sử dụng gợi ý cho ứng dụng. Để bật gợi ý cho ứng dụng, trang web phải 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ả các yêu cầu tài nguyên phụ. Trong tương lai, chúng tôi có thể cung cấp thêm một cơ chế để duy trì lựa chọn ưu tiên này cho một nguồn gốc cụ thể, cho phép phân phối cùng một gợi ý trên các yêu cầu điều hướng.

  3. Tại sao chúng ta cần gợi ý ứ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 là không gây ra các lượt truy cập trả về tốn kém và làm chậm đá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 trong yêu cầu.

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

  5. Một số yêu cầu hình ảnh không báo cáo Chiều rộng, tại sao? Trình duyệt có thể không biết chiều rộng hiển thị dự kiến vì trang web đang 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 đó và đối 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 được gợi ý về Chiều rộng, hãy nhớ chỉ định giá trị kích thước trên hình ảnh.

  6. 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 – hãy xem phần "Báo cáo chức năng bằng ServiceWorker". Các 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 tuý 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ạ và tìm hiểu thêm về những gì khác ở đâu? Hãy xem tài liệu giải thíchmở vấn đề trên GitHub nếu bạn có ý kiến phản hồi hoặc câu hỏi khác.