Mengotomatiskan pemilihan resource dengan petunjuk klien

Membuat aplikasi untuk web memberi Anda jangkauan yang tak tertandingi. Aplikasi web Anda dapat diakses dengan mudah di hampir setiap perangkat, seperti smartphone, tablet, laptop dan desktop, TV, dan masih banyak lagi, terlepas dari merek atau platformnya. Untuk memberikan pengalaman terbaik, Anda telah membuat situs responsif yang menyesuaikan presentasi dan fungsi untuk setiap faktor bentuk. Sekarang, Anda menjalankan checklist performa untuk memastikan aplikasi dimuat secepat mungkin: Anda telah mengoptimalkan jalur rendering penting, Anda telah mengompresi dan meng-cache resource teks, dan sekarang Anda melihat sebagian besar resource gambar, yang sering kali ditransfer. Masalahnya, pengoptimalan gambar itu sulit:

  • Menentukan format yang sesuai (vektor vs. raster)
  • Menentukan format encoding yang optimal (jpeg, webp, dll.)
  • Tentukan setelan kompresi yang tepat (lossy vs. lossless)
  • Menentukan metadata mana yang harus disimpan atau dihapus
  • Buat beberapa varian dari masing-masing layar untuk setiap layar + resolusi DPR
  • ...
  • Memperhitungkan jenis, kecepatan, dan preferensi jaringan pengguna

Secara individual, masalah ini merupakan masalah yang dapat dipahami dengan baik. Secara kolektif, mereka menciptakan ruang pengoptimalan besar yang sering kali kami (developer) abaikan atau abaikan. Manusia melakukan pekerjaan yang buruk untuk menjelajahi ruang penelusuran yang sama secara berulang, terutama jika ada banyak langkah yang terlibat. Di sisi lain, komputer unggul dalam jenis tugas ini.

Jawaban atas strategi pengoptimalan yang baik dan berkelanjutan untuk gambar, dan resource lain dengan properti serupa adalah sederhana: otomatisasi. Jika Anda secara manual menyesuaikan resource, Anda tidak akan melakukan kesalahan: Anda akan lupa, Anda akan menjadi malas, atau orang lain pasti akan membuat kesalahan ini untuk Anda.

Saga developer yang sadar performa

Penelusuran melalui ruang pengoptimalan gambar memiliki dua fase berbeda: waktu build dan waktu proses.

  • Beberapa pengoptimalan bersifat intrinsik pada resource itu sendiri - misalnya memilih format dan jenis encoding yang sesuai, menyesuaikan setelan kompresi untuk setiap encoder, menghapus metadata yang tidak diperlukan, dan sebagainya. Langkah-langkah ini dapat dilakukan pada "waktu build".
  • Pengoptimalan lainnya ditentukan oleh jenis dan properti klien yang memintanya dan harus dilakukan pada "run-time": memilih resource yang sesuai untuk DPR klien dan lebar tampilan yang diinginkan, dengan memperhitungkan kecepatan jaringan klien, preferensi pengguna dan aplikasi, dan seterusnya.

Alat waktu build ada, tetapi masih dapat ditingkatkan kualitasnya. Misalnya, ada banyak penghematan yang bisa didapat dengan menyesuaikan setelan "kualitas" secara dinamis untuk setiap gambar dan setiap format gambar, tetapi belum ada orang yang benar-benar menggunakannya di luar riset. Bidang ini siap untuk inovasi, tetapi untuk tujuan postingan ini, saya tidak akan mengubah apa pun. Mari kita fokus pada bagian {i>run-time<i} dari cerita.

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

Intent aplikasi sangat sederhana: mengambil dan menampilkan gambar pada 50% dari area tampilan pengguna. Ini adalah saat di mana hampir setiap desainer mencuci tangan dan kepala mereka ke bar. Sementara itu, developer yang sadar performa di tim sedang berlama-lama:

  1. Untuk mendapatkan kompresi terbaik, ia ingin menggunakan format gambar yang optimal untuk setiap klien: WebP untuk Chrome, JPEG XR untuk Edge, dan JPEG untuk lainnya.
  2. Untuk mendapatkan kualitas visual terbaik, dia perlu menghasilkan beberapa varian dari setiap gambar dengan resolusi berbeda: 1x, 1,5x, 2x, 2,5x, 3x, dan bahkan mungkin beberapa di antaranya.
  3. Untuk menghindari pengiriman piksel yang tidak perlu, ia perlu memahami apa arti "50% dari area pandang pengguna"—ada banyak perbedaan lebar area pandang yang tersedia.
  4. Idealnya, dia juga ingin memberikan pengalaman yang andal ketika pengguna di jaringan yang lebih lambat akan otomatis mengambil resolusi yang lebih rendah. Lagi pula, ini semua tentang waktu untuk berlatih.
  5. Aplikasi ini juga mengekspos beberapa kontrol pengguna yang memengaruhi resource gambar mana yang harus diambil, jadi ada hal yang juga perlu dipertimbangkan.

Oh, kemudian desainer menyadari bahwa dia perlu menampilkan gambar yang berbeda pada lebar 100% jika ukuran area pandang kecil untuk mengoptimalkan keterbacaan. Artinya, sekarang kita harus mengulangi proses yang sama untuk satu aset lagi, lalu membuat pengambilan bergantung pada ukuran area pandang. Pernahkah saya menyebutkan bahwa hal ini sulit? Baiklah, mari kita mulai. Elemen picture akan membawa kita cukup banyak:

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

Kami telah menangani arah gambar, pemilihan format, dan menyediakan enam varian setiap gambar untuk memperhitungkan variabilitas dalam DPR dan lebar area tampilan perangkat klien. Luar biasa!

Sayangnya, elemen picture tidak memungkinkan kita untuk menentukan aturan apa pun tentang perilakunya berdasarkan kecepatan atau jenis koneksi klien. Meskipun demikian, algoritma pemrosesannya memungkinkan agen pengguna menyesuaikan resource yang diambil dalam beberapa kasus—lihat langkah 5. Kita hanya perlu berharap bahwa agen pengguna cukup cerdas. (Catatan: semua implementasi saat ini tidak ada). Demikian pula, tidak ada hook dalam elemen picture untuk memungkinkan logika khusus aplikasi yang memperhitungkan preferensi aplikasi atau pengguna. Untuk mendapatkan dua bit terakhir ini, kita harus memindahkan semua logika di atas ke JavaScript, tetapi hal tersebut akan kehilangan pengoptimalan pemindai pramuat yang ditawarkan oleh picture. Hmm.

Terlepas dari batasan-batasan itu, itu berhasil. Yah, setidaknya untuk aset ini. Tantangan yang sebenarnya dan jangka panjang di sini adalah kita tidak dapat mengharapkan desainer atau developer untuk membuat kode seperti ini untuk setiap aset. Ini adalah teka-teki asah otak yang menyenangkan pada percobaan pertama, tetapi segera kehilangan daya tariknya. Kita membutuhkan otomatisasi. Mungkin alat IDE atau transformasi konten lainnya dapat menyelamatkan kita dan membuat boilerplate di atas secara otomatis.

Mengotomatiskan pemilihan resource dengan client hints

Tarik napas dalam-dalam, tangguhkan ketidakpercayaan Anda, dan sekarang pertimbangkan contoh berikut:

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

Percaya atau tidak, contoh di atas sudah cukup untuk memberikan semua kemampuan yang sama seperti markup gambar yang jauh lebih panjang di atas. Selain itu, seperti yang akan kita lihat, contoh ini memungkinkan developer mengontrol sepenuhnya cara dan resource gambar diambil. "Magic" ada di baris pertama yang mengaktifkan pelaporan client hints dan memberi tahu browser untuk memperlihatkan rasio piksel perangkat (DPR), lebar area pandang tata letak (Viewport-Width), dan lebar tampilan yang diinginkan (Width) resource ke server.

Dengan mengaktifkan client hints, markup sisi klien yang dihasilkan hanya mempertahankan persyaratan presentasi. Desainer tidak perlu mengkhawatirkan jenis gambar, resolusi klien, titik henti sementara optimal untuk mengurangi byte yang dikirimkan, atau kriteria pemilihan resource lainnya. Jujur saja, mereka tidak pernah melakukannya, dan seharusnya tidak perlu melakukannya. Lebih baik lagi, developer juga tidak perlu menulis ulang dan memperluas markup di atas karena pemilihan resource sebenarnya dinegosiasikan oleh klien dan server.

Chrome 46 menyediakan dukungan native untuk petunjuk DPR, Width, dan Viewport-Width. Petunjuk dinonaktifkan secara default dan <meta http-equiv="Accept-CH" content="..."> di atas berfungsi sebagai sinyal keikutsertaan yang memberi tahu Chrome untuk menambahkan header yang ditentukan ke permintaan keluar. Dengan demikian, mari kita periksa header permintaan dan respons untuk contoh permintaan gambar:

Diagram negosiasi petunjuk klien

Chrome mengiklankan dukungannya untuk format WebP melalui header permintaan Accept; browser Edge baru juga mengiklankan dukungan untuk JPEG XR melalui header Accept.

Tiga header permintaan berikutnya adalah header petunjuk klien yang mengiklankan rasio piksel perangkat dari perangkat klien (3x), lebar area tampilan tata letak (460 px), dan lebar tampilan resource yang diinginkan (230 px). Hal ini memberikan semua informasi yang diperlukan ke server untuk memilih varian gambar yang optimal berdasarkan kumpulan kebijakannya sendiri: ketersediaan resource yang telah dibuat sebelumnya, biaya encoding ulang atau ukuran resource, popularitas resource, beban server saat ini, dan sebagainya. Dalam kasus khusus ini, server menggunakan petunjuk DPR dan Width serta menampilkan resource WebP, seperti yang ditunjukkan oleh header Content-Type, Content-DPR, dan Vary.

Tidak ada keajaiban di sini. Kita telah memindahkan pemilihan resource dari markup HTML ke negosiasi permintaan-respons antara klien dan server. Akibatnya, HTML hanya berfokus pada persyaratan presentasi dan dapat dipercaya oleh desainer dan developer mana pun untuk ditulis, sedangkan penelusuran melalui ruang pengoptimalan gambar dialihkan ke komputer dan kini dengan mudah diotomatiskan dalam skala besar. Ingat developer kita yang sadar performa? Tugasnya sekarang adalah menulis layanan gambar yang dapat memanfaatkan petunjuk yang diberikan dan menampilkan respons yang sesuai: dia dapat menggunakan bahasa atau server apa pun yang dia suka, atau membiarkan layanan pihak ketiga atau CDN melakukannya untuknya.

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

Dan, ingat orang di atas? Dengan client hints, tag gambar yang sederhana kini menjadi DPR, area tampilan, dan berbasis lebar tanpa markup tambahan. Jika perlu menambahkan arah gambar, Anda dapat menggunakan tag picture, seperti yang kita ilustrasikan di atas, dan jika tidak, semua tag gambar yang ada akan menjadi jauh lebih cerdas. Client hints meningkatkan elemen img dan picture yang ada.

Mengambil kontrol atas pemilihan resource dengan pekerja layanan

ServiceWorker, sebenarnya, adalah proxy sisi klien yang berjalan di browser Anda. Alat ini mencegat semua permintaan keluar dan memungkinkan Anda memeriksa, menulis ulang, meng-cache, dan bahkan mensintesis respons. Gambar pun demikian, dan jika client hints diaktifkan, ServiceWorker aktif dapat mengidentifikasi permintaan gambar, memeriksa petunjuk klien yang diberikan, dan menentukan logika pemrosesannya sendiri.

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])
    }
    ...
}
ServiceWorker petunjuk klien.

ServiceWorker memberi Anda kontrol penuh sisi klien atas pemilihan resource. Langkah ini sangat penting. Pertimbangkan baik-baik, karena kemungkinannya mendekati tak terbatas:

  • Anda dapat menulis ulang nilai header client hints yang ditetapkan oleh agen pengguna.
  • Anda dapat menambahkan nilai header client hints baru ke permintaan.
  • Anda dapat menulis ulang URL dan mengarahkan permintaan gambar ke server alternatif (misalnya, CDN).
    • Anda bahkan dapat memindahkan nilai petunjuk dari header ke URL itu sendiri jika hal tersebut memudahkan deployment di infrastruktur Anda.
  • Anda dapat menyimpan respons ke dalam cache dan menentukan logika sendiri untuk resource mana yang disajikan.
  • Anda dapat menyesuaikan respons berdasarkan konektivitas pengguna.
  • Anda dapat memperhitungkan penggantian preferensi pengguna dan aplikasi.
  • Kamu bisa... melakukan apa pun yang diinginkan hatimu.

Elemen picture memberikan kontrol arah gambar yang diperlukan dalam markup HTML. Client hints memberikan anotasi pada permintaan gambar yang dihasilkan, yang mengaktifkan otomatisasi pemilihan resource. ServiceWorker menyediakan kemampuan pengelolaan permintaan dan respons pada klien. Inilah contohnya Extensible Web.

FAQ client hints

  1. Di mana client hints tersedia? Dikirim dalam Chrome 46. Dalam pertimbangan di Firefox dan Edge.

  2. Mengapa client hints memilih ikut serta? Kami ingin meminimalkan overhead untuk situs yang tidak akan menggunakan client hints. Untuk mengaktifkan petunjuk klien, situs harus menyediakan header Accept-CH atau perintah <meta http-equiv> yang setara di markup halaman. Dengan adanya salah satunya, agen pengguna akan menambahkan petunjuk yang sesuai ke semua permintaan subresource. Di masa mendatang, kami mungkin akan memberikan mekanisme tambahan untuk mempertahankan preferensi ini untuk origin tertentu, yang akan memungkinkan petunjuk yang sama dikirimkan pada permintaan navigasi.

  3. Mengapa kita memerlukan petunjuk klien jika memiliki ServiceWorker? ServiceWorker tidak memiliki akses ke informasi tata letak, resource, dan lebar area tampilan. Setidaknya, tidak tanpa menimbulkan perjalanan bolak-balik yang mahal dan menunda permintaan gambar secara signifikan, misalnya, saat permintaan gambar dimulai oleh parser pramuat. Client hints terintegrasi dengan browser untuk membuat data ini tersedia sebagai bagian dari permintaan.

  4. Apakah client hints untuk resource gambar saja? Kasus penggunaan inti di balik petunjuk DPR, Lebar Area Pandang, dan Lebar adalah mengaktifkan pemilihan resource untuk aset gambar. Namun, petunjuk yang sama dikirimkan untuk semua subresource apa pun jenisnya -- misalnya, permintaan CSS dan JavaScript juga akan mendapatkan informasi yang sama dan juga dapat digunakan untuk mengoptimalkan resource tersebut.

  5. Beberapa permintaan gambar tidak melaporkan Lebar, mengapa demikian? Browser mungkin tidak mengetahui lebar tampilan yang diinginkan karena situs mengandalkan ukuran intrinsik gambar. Oleh karena itu, petunjuk Lebar dihapus untuk permintaan tersebut, dan untuk permintaan yang tidak memiliki "lebar tampilan" - mis. resource JavaScript. Untuk menerima Petunjuk lebar, pastikan untuk menentukan nilai ukuran pada gambar Anda.

  6. Bagaimana dengan <insert my favorite hint>? ServiceWorker memungkinkan developer menangkap dan mengubah (misalnya, menambahkan header baru) semua permintaan keluar. Sebagai contoh, mudah untuk menambahkan informasi berbasis NetInfo untuk menunjukkan jenis koneksi saat ini -- lihat "Pelaporan kemampuan dengan ServiceWorker". Petunjuk "native" yang dikirimkan di Chrome (DPR, Lebar, Lebar Resource) diimplementasikan di browser karena implementasi murni berbasis SW akan menunda semua permintaan gambar.

  7. Di mana saya bisa mempelajari lebih lanjut, melihat lebih banyak demo, dan bagaimana? Lihat dokumen penjelasan dan jangan ragu untuk membuka masalah di GitHub jika ada masukan atau pertanyaan lain.