Mengotomatiskan pemilihan resource dengan petunjuk klien

Mem-build untuk web memberi Anda jangkauan yang tak tertandingi. Aplikasi web Anda hanya selangkah lagi dan tersedia di hampir semua perangkat terhubung-smartphone, tablet, laptop dan desktop, TV, dan lainnya-terlepas dari merek atau platformnya. Untuk memberikan pengalaman terbaik, Anda telah membuat situs responsif yang menyesuaikan presentasi dan fungsi untuk setiap faktor bentuk, dan sekarang Anda menjalankan checklist performa untuk memastikan aplikasi dimuat sesegera mungkin: Anda telah mengoptimalkan jalur rendering penting, Anda telah mengompresi dan meng-cache resource teks, dan sekarang Anda melihat resource gambar, yang sering kali menyumbang sebagian besar byte yang ditransfer. Masalahnya, pengoptimalan gambar sulit:

  • Menentukan format yang sesuai (vektor vs. raster)
  • Menentukan format encoding yang optimal (jpeg, webp, dll.)
  • Menentukan setelan kompresi yang tepat (lossy vs. lossless)
  • Menentukan metadata yang harus disimpan atau dihapus
  • Membuat beberapa varian untuk setiap resolusi DPR + tampilan
  • ...
  • Mempertimbangkan jenis, kecepatan, dan preferensi jaringan pengguna

Masing-masing adalah masalah yang dipahami dengan baik. Secara kolektif, hal-hal tersebut menciptakan ruang pengoptimalan yang besar yang sering kali kita (developer) abaikan atau lalai. Manusia tidak dapat menjelajahi ruang penelusuran yang sama secara berulang, terutama jika banyak langkah yang terlibat. Di sisi lain, komputer sangat unggul dalam jenis tugas ini.

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

Kisah developer yang mengutamakan performa

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

  • Beberapa pengoptimalan bersifat intrinsik untuk 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 "waktu proses": memilih resource yang sesuai untuk DPR klien dan lebar tampilan yang diinginkan, dengan mempertimbangkan kecepatan jaringan klien, preferensi pengguna dan aplikasi, dan sebagainya.

Alat build-time ada, tetapi dapat ditingkatkan kualitasnya. Misalnya, ada banyak penghematan yang dapat diperoleh dengan menyesuaikan setelan "kualitas" secara dinamis untuk setiap gambar dan setiap format gambar, tetapi saya belum melihat siapa pun yang benar-benar menggunakannya di luar riset. Ini adalah area yang tepat untuk inovasi, tetapi untuk tujuan postingan ini, saya akan membiarkannya begitu saja. Mari kita fokus pada bagian runtime 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% tampilan yang terlihat pengguna. Di sinilah sebagian besar desainer mencuci tangan dan menuju ke bar. Sementara itu, developer yang sadar performa dalam tim akan mengalami malam yang panjang:

  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 yang lainnya.
  2. Untuk mendapatkan kualitas visual terbaik, ia perlu membuat beberapa varian setiap gambar pada resolusi yang berbeda: 1x, 1,5x, 2x, 2,5x, 3x, dan mungkin beberapa lagi di antaranya.
  3. Untuk menghindari pengiriman piksel yang tidak perlu, ia perlu memahami arti "50% area pandang pengguna sebenarnya"—ada banyak lebar area pandang yang berbeda di luar sana.
  4. Idealnya, ia juga ingin memberikan pengalaman yang tangguh, yaitu pengguna di jaringan yang lebih lambat akan otomatis mengambil resolusi yang lebih rendah. Lagi pula, waktunya untuk membuat kaca.
  5. Aplikasi juga mengekspos beberapa kontrol pengguna yang memengaruhi resource gambar yang harus diambil, jadi ada faktor lain yang perlu dipertimbangkan.

Kemudian, desainer menyadari bahwa ia perlu menampilkan gambar yang berbeda dengan 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 bersyarat pada ukuran area pandang. Sudahkah saya menyebutkan bahwa hal ini sulit? Baiklah, mari kita mulai. Elemen picture akan membawa 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 seni, pemilihan format, dan menyediakan enam varian setiap gambar untuk memperhitungkan variabilitas dalam DPR dan lebar area pandang perangkat klien. Luar biasa!

Sayangnya, elemen picture tidak memungkinkan kita menentukan aturan apa pun untuk perilakunya 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 harus berharap bahwa agen pengguna cukup pintar. (Catatan: tidak ada implementasi saat ini yang memenuhi syarat). 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 itu akan menghilangkan pengoptimalan pemindai pramuat yang ditawarkan oleh picture. Hmm.

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

Mengotomatiskan pemilihan resource dengan petunjuk klien

Tarik napas dalam-dalam, tahan 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 dengan markup gambar yang jauh lebih panjang di atas, plus, seperti yang akan kita lihat, contoh ini memungkinkan kontrol developer penuh atas cara, mana, dan kapan resource gambar diambil. "Keajaiban" ada di baris pertama yang mengaktifkan pelaporan client hint dan memberi tahu browser untuk mengiklankan rasio piksel perangkat (DPR), lebar area pandang tata letak (Viewport-Width), dan lebar tampilan yang diinginkan (Width) resource ke server.

Dengan mengaktifkan petunjuk klien, markup sisi klien yang dihasilkan hanya mempertahankan persyaratan presentasi. Desainer tidak perlu khawatir dengan jenis gambar, resolusi klien, titik henti sementara yang optimal untuk mengurangi byte yang dikirim, atau kriteria pemilihan resource lainnya. Mari kita hadapi kenyataan, mereka tidak pernah melakukannya, dan mereka tidak perlu melakukannya. Lebih baik lagi, developer juga tidak perlu menulis ulang dan memperluas markup di atas karena pemilihan resource yang 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. Setelah itu, 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 perangkat klien (3x), lebar tampilan yang terlihat (460 piksel), dan lebar tampilan yang diinginkan dari resource (230 piksel). Hal ini memberikan semua informasi yang diperlukan ke server untuk memilih varian gambar yang optimal berdasarkan kumpulan kebijakannya sendiri: ketersediaan resource yang dibuat sebelumnya, biaya enkode ulang atau mengubah 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 sihir di sini. Kami memindahkan pemilihan resource dari markup HTML dan ke dalam negosiasi permintaan-respons antara klien dan server. Akibatnya, HTML hanya berkaitan dengan persyaratan presentasi dan merupakan sesuatu yang dapat kita percayakan kepada desainer dan developer untuk ditulis, sementara penelusuran melalui ruang pengoptimalan gambar ditangguhkan ke komputer dan kini mudah dilakukan secara otomatis dalam skala besar. Ingat developer yang mengutamakan performa? Tugasnya sekarang adalah menulis layanan gambar yang dapat memanfaatkan petunjuk yang diberikan dan menampilkan respons yang sesuai: ia dapat menggunakan bahasa atau server apa pun yang ia sukai, atau, membiarkan layanan pihak ketiga atau CDN melakukannya atas namanya.

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

Selain itu, ingat orang di atas? Dengan petunjuk klien, tag gambar sederhana kini mendukung DPR, area pandang, dan lebar tanpa markup tambahan. Jika perlu menambahkan art-direction, Anda dapat menggunakan tag picture, seperti yang kami ilustrasikan di atas, dan jika tidak, semua tag gambar yang ada akan menjadi jauh lebih cerdas. Petunjuk klien meningkatkan elemen img dan picture yang ada.

Mengontrol pemilihan resource dengan pekerja layanan

ServiceWorker, pada dasarnya, adalah proxy sisi klien yang berjalan di browser Anda. Fitur ini mencegat semua permintaan keluar dan memungkinkan Anda memeriksa, menulis ulang, meng-cache, dan bahkan menyintesis respons. Gambar tidak berbeda dan, dengan mengaktifkan petunjuk klien, 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])
    }
    ...
}
Petunjuk klien serviceWorker.

ServiceWorker memberi Anda kontrol sisi klien penuh atas pemilihan resource. Hal ini sangat penting. Pahami hal ini, karena kemungkinannya hampir tak terbatas:

  • Anda dapat menulis ulang nilai header petunjuk klien yang ditetapkan 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 URL itu sendiri jika hal itu mempermudah deployment di infrastruktur Anda.
  • Anda dapat meng-cache respons dan menentukan logika sendiri untuk resource yang ditayangkan.
  • Anda dapat menyesuaikan respons berdasarkan konektivitas pengguna.
  • Anda dapat memperhitungkan penggantian preferensi aplikasi dan pengguna.
  • Anda dapat … melakukan apa pun yang Anda inginkan.

Elemen picture memberikan kontrol arah seni yang diperlukan dalam markup HTML. Petunjuk klien memberikan anotasi pada permintaan gambar yang dihasilkan yang memungkinkan otomatisasi pemilihan resource. ServiceWorker menyediakan kemampuan pengelolaan permintaan dan respons di klien. Ini adalah penerapan web yang dapat diperluas.

FAQ petunjuk klien

  1. Di mana petunjuk klien tersedia? Dikirim dalam Chrome 46. Sedang dipertimbangkan di Firefox dan Edge.

  2. Mengapa petunjuk klien diikutsertakan? Kami ingin meminimalkan overhead untuk situs yang tidak akan menggunakan petunjuk klien. Untuk mengaktifkan petunjuk klien, situs harus memberikan header Accept-CH atau perintah <meta http-equiv> yang setara dalam markup halaman. Dengan adanya salah satu dari keduanya, agen pengguna akan menambahkan petunjuk yang sesuai ke semua permintaan sub-resource. Di masa mendatang, kami dapat menyediakan 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 lebar area pandang, resource, dan tata letak. Setidaknya, tidak tanpa memperkenalkan roundtrip yang mahal dan menunda permintaan gambar secara signifikan - misalnya, saat permintaan gambar dimulai oleh parser pramuat. Petunjuk klien terintegrasi dengan browser untuk menyediakan data ini sebagai bagian dari permintaan.

  4. Apakah petunjuk klien hanya untuk resource gambar? Kasus penggunaan inti di balik petunjuk DPR, Viewport-Width, dan Width adalah untuk 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 dapat digunakan untuk mengoptimalkan resource tersebut juga.

  5. Beberapa permintaan gambar tidak melaporkan Lebar, mengapa? Browser mungkin tidak mengetahui lebar tampilan yang diinginkan karena situs mengandalkan ukuran intrinsik gambar. Akibatnya, petunjuk Lebar 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 my favorite hint>? ServiceWorker memungkinkan developer untuk mencegat dan mengubah (misalnya, menambahkan header baru) semua permintaan keluar. Misalnya, mudah untuk menambahkan informasi berbasis NetInfo untuk menunjukkan jenis koneksi saat ini -- lihat "Pelaporan kemampuan dengan ServiceWorker". Petunjuk "native" yang dikirimkan di Chrome (DPR, Width, Resource-Width) diimplementasikan di browser karena implementasi berbasis SW murni akan menunda semua permintaan gambar.

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