Mengotomatiskan pemilihan resource dengan petunjuk klien

Ilya Grigorik

Membuat aplikasi untuk web memberi Anda jangkauan yang tak tertandingi. Aplikasi web Anda dapat diakses dengan mudah dan tersedia di hampir setiap perangkat yang terhubung seperti smartphone, tablet, laptop dan desktop, TV, dan masih banyak lagi, terlepas dari merek atau platform yang digunakan. Untuk memberikan pengalaman terbaik, Anda telah membuat situs responsif yang menyesuaikan presentasi dan fungsionalitas untuk setiap faktor bentuk. Selanjutnya, sekarang Anda menjalankan checklist performa untuk memastikan aplikasi dimuat secepat mungkin: Anda telah mengoptimalkan jalur rendering penting, Anda telah memadatkan dan meng-cache resource teks, dan sekarang Anda sedang melihat sebagian besar byte yang ditransfer. Masalahnya adalah, pengoptimalan gambar 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 dihilangkan
  • Buat beberapa varian untuk masing-masing layar + resolusi DPR
  • ...
  • Perhitungkan jenis, kecepatan, dan preferensi jaringan pengguna

Secara individual, ini adalah masalah yang dipahami dengan baik. Secara kolektif, solusi ini membuat ruang pengoptimalan besar yang sering kami (developer) abaikan atau abaikan. Manusia melakukan pekerjaan yang buruk dalam menjelajahi ruang penelusuran yang sama secara berulang, terutama jika ada banyak langkah yang terlibat. Komputer, di sisi lain, unggul dalam jenis tugas ini.

Jawaban atas strategi pengoptimalan yang baik dan berkelanjutan untuk gambar, dan resource lain dengan properti serupa itu sederhana: otomatisasi. Jika Anda menyelaraskan resource, Anda tidak akan melakukan kesalahan: Anda akan lupa, Anda akan menjadi malas, atau orang lain akan membuat kesalahan ini untuk Anda - dijamin.

Kisah developer yang sadar kinerja

Ruang penelusuran melalui pengoptimalan gambar memiliki dua fase yang 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 perlu, 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, memperhitungkan kecepatan jaringan klien, preferensi pengguna dan aplikasi, dan sebagainya.

Alat waktu build sudah ada, tetapi dapat ditingkatkan kualitasnya. Misalnya, ada banyak penghematan yang bisa diperoleh dengan menyesuaikan setelan "kualitas" secara dinamis untuk setiap gambar dan setiap format gambar. Namun, kami belum melihat ada orang yang menggunakannya di luar riset. Bagian ini adalah area yang memerlukan inovasi, tetapi untuk tujuan postingan ini, saya akan membahasnya saja. Mari kita fokus pada bagian run-time dari cerita ini.

<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% area pandang pengguna. Di sinilah sebagian besar desainer mencuci tangan dan kepala mereka untuk bar. Sementara itu, developer yang memperhatikan performa di dalam tim akan menikmati malam yang panjang:

  1. Untuk mendapatkan kompresi terbaik, dia ingin menggunakan format gambar yang optimal untuk setiap klien: WebP untuk Chrome, JPEG XR untuk Edge, dan JPEG untuk sisanya.
  2. Untuk mendapatkan kualitas visual terbaik, dia perlu menghasilkan beberapa varian setiap gambar pada resolusi yang berbeda: 1x, 1,5x, 2x, 2,5x, 3x, dan bahkan beberapa di antaranya.
  3. Untuk menghindari pengiriman piksel yang tidak diperlukan, dia perlu memahami "sebenarnya 50% dari area pandang pengguna"—ada banyak lebar area pandang yang berbeda di luar sana.
  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 saatnya untuk memakai kacamata.
  5. Aplikasi ini juga mengekspos beberapa kontrol pengguna yang memengaruhi resource gambar mana yang harus diambil, sehingga ada juga yang harus diperhitungkan.

Kemudian, desainer tersebut menyadari bahwa dia perlu menampilkan gambar yang berbeda dengan lebar 100% jika ukuran area pandang kecil untuk mengoptimalkan keterbacaan. Artinya, kita sekarang harus mengulangi proses yang sama untuk satu aset lagi, lalu membuat pengambilan menjadi kondisional pada ukuran area pandang. Apakah saya sudah mengatakan bahwa hal ini sulit? Mari kita lanjutkan. Elemen picture akan memperoleh kita cukup jauh:

<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 pandang perangkat klien. Mengesankan!

Sayangnya, elemen picture tidak memungkinkan kita untuk menentukan aturan tentang cara perilakunya harus berperilaku berdasarkan jenis atau kecepatan koneksi klien. Meskipun demikian, algoritma pemrosesannya memungkinkan agen pengguna menyesuaikan resource yang diambilnya dalam beberapa kasus—lihat langkah 5. Kita hanya perlu berharap bahwa agen pengguna cukup cerdas. (Catatan: tidak satu pun implementasi saat ini). 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 dalam JavaScript, tetapi hal itu akan kehilangan pengoptimalan pemindai pramuat yang ditawarkan oleh picture. Hmm.

Di samping keterbatasan itu, cara ini berhasil. Ya, setidaknya untuk aset tertentu ini. Tantangan jangka panjang dan yang sebenarnya adalah kita tidak dapat mengharapkan desainer atau developer membuat kode seperti ini secara langsung untuk setiap aset. Game ini berupa teka-teki asah otak yang seru di percobaan pertama, tetapi langsung kehilangan daya tariknya setelahnya. Kita membutuhkan otomatisasi. Mungkin IDE atau alat transformasi konten lainnya dapat menyelamatkan kita dan secara otomatis menghasilkan boilerplate di atas.

Mengotomatiskan pemilihan resource dengan petunjuk klien

Tarik napas dalam-dalam, tunda 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 cukup untuk memberikan semua kemampuan yang sama seperti markup gambar yang jauh lebih panjang di atas ditambah, seperti yang akan kita lihat, contoh di memungkinkan kontrol penuh developer atas cara, yang mana, dan kapan resource gambar diambil. "magic" berada di baris pertama yang mengaktifkan pelaporan client hints dan memberi tahu browser untuk mengiklankan rasio piksel perangkat (DPR), lebar area pandang tata letak (Viewport-Width), dan lebar tampilan yang diinginkan (Width) dari resource ke server.

Jika petunjuk klien diaktifkan, markup sisi klien yang dihasilkan hanya akan mempertahankan persyaratan presentasi. Desainer tidak perlu mengkhawatirkan jenis gambar, resolusi klien, titik henti sementara optimal untuk mengurangi byte yang dikirim, atau kriteria pemilihan resource lainnya. Kita hadapi itu, mereka tidak pernah melakukannya, dan seharusnya tidak harus. 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 memberikan 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. Setelah melakukannya, mari kita periksa header permintaan dan respons untuk permintaan gambar contoh:

Diagram negosiasi petunjuk klien

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

Tiga header permintaan berikutnya adalah header petunjuk klien yang mengiklankan rasio piksel perangkat perangkat klien (3x), lebar area pandang tata letak (460 piksel), dan lebar tampilan resource yang diinginkan (230 piksel). Tindakan ini memberikan semua informasi yang diperlukan kepada server untuk memilih varian gambar yang optimal berdasarkan rangkaian kebijakannya sendiri: ketersediaan resource yang dibuat sebelumnya, biaya encoding ulang atau pengubahan ukuran resource, popularitas resource, beban server saat ini, dan seterusnya. 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. Kami memindahkan pilihan resource dari markup HTML ke negosiasi permintaan-respons antara klien dan server. Akibatnya, HTML hanya terkait dengan persyaratan presentasi dan kami dapat mempercayai desainer dan pengembang untuk menulisnya, sementara penelusuran melalui ruang pengoptimalan gambar dialihkan ke komputer dan sekarang dengan mudah diotomatiskan dalam skala besar. Ingat developer kami 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 sukai, atau membiarkan layanan pihak ketiga atau CDN melakukan ini atas namanya.

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

Dan, ingat orang yang di atas? Dengan petunjuk klien, tag gambar sederhana kini menjadi DPR, area pandang, dan peka lebar tanpa markup tambahan. Jika perlu menambahkan art-direction, Anda dapat menggunakan tag picture, seperti yang kita ilustrasi di atas, dan jika tidak, semua tag gambar yang ada menjadi jauh lebih cerdas. Client hints meningkatkan elemen img dan picture yang ada.

Mengambil kontrol atas pemilihan resource dengan pekerja layanan

Pada dasarnya, ServiceWorker adalah proxy sisi klien yang berjalan di browser Anda. Fungsi ini mencegat semua permintaan keluar dan memungkinkan Anda memeriksa, menulis ulang, meng-cache, dan bahkan menyintesis respons. Gambar tidak berbeda dan, dengan petunjuk klien 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 pada sisi klien atas pemilihan resource. Langkah ini sangat penting. Biarkan hal tersebut dipahami, karena kemungkinannya hampir tak terbatas:

  • Anda dapat menulis ulang nilai header client hints yang disetel oleh agen pengguna.
  • Anda dapat menambahkan nilai header petunjuk klien 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 dan ke dalam URL itu sendiri jika hal tersebut memudahkan deployment di infrastruktur Anda.
  • Anda bisa meng-cache respons dan menentukan logika sendiri untuk resource mana yang ditayangkan.
  • Anda dapat menyesuaikan respons berdasarkan konektivitas pengguna.
  • Anda dapat memperhitungkan penggantian preferensi pengguna dan aplikasi.
  • Sebenarnya, kamu bisa ... melakukan apa pun yang diinginkan hatimu.

Elemen picture menyediakan kontrol arah gambar yang diperlukan dalam markup HTML. Client hints menyediakan anotasi pada permintaan gambar yang dihasilkan yang mengaktifkan otomatisasi pemilihan resource. ServiceWorker memberikan kemampuan pengelolaan permintaan dan respons pada klien. Inilah cara kerja web yang dapat diperluas.

FAQ petunjuk klien

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

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

  3. Mengapa kita memerlukan client hints jika memiliki ServiceWorker? ServiceWorker tidak memiliki akses ke informasi tata letak, resource, dan lebar area pandang. Setidaknya, bukan tanpa menimbulkan perjalanan bolak-balik yang mahal dan secara signifikan menunda permintaan gambar - mis., 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 hanya untuk resource gambar? 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 terlepas dari jenisnya -- misalnya, permintaan CSS dan JavaScript juga mendapatkan informasi yang sama dan juga dapat digunakan untuk mengoptimalkan resource tersebut.

  5. Beberapa permintaan gambar tidak melaporkan Lebar, mengapa? Browser mungkin tidak mengetahui lebar tampilan yang dimaksudkan karena situs bergantung pada ukuran intrinsik gambar. Akibatnya, petunjuk Lebar tidak akan dihilangkan untuk permintaan tersebut, dan untuk permintaan yang tidak memiliki "lebar tampilan" - misalnya resource JavaScript. Untuk menerima petunjuk Lebar, pastikan untuk menentukan nilai ukuran pada gambar Anda.

  6. Bagaimana dengan <insert myfavorite 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) diterapkan di browser karena penerapan berbasis SW murni akan menunda semua permintaan gambar.

  7. Di mana saya bisa mempelajari lebih lanjut, melihat demo lainnya, dan bagaimana? Lihat dokumen penjelasan dan jangan ragu untuk melaporkan masalah di GitHub jika Anda memiliki masukan atau pertanyaan lainnya.