Komponen gambar mengenkapsulasi praktik terbaik performa dan memberikan solusi siap pakai untuk mengoptimalkan gambar.
Gambar merupakan sumber umum bottleneck performa untuk aplikasi web dan area fokus utama untuk pengoptimalan. Gambar yang tidak dioptimalkan berkontribusi pada penggelembungan halaman dan menyumbang lebih dari 70% total berat halaman dalam byte pada persentil ke-90th. Beberapa cara untuk mengoptimalkan gambar memerlukan "komponen gambar" cerdas dengan solusi performa yang disertakan sebagai default.
Tim Aurora bekerja sama dengan Next.js untuk membuat salah satu komponen tersebut. Tujuannya adalah membuat template gambar yang dioptimalkan yang dapat disesuaikan lebih lanjut oleh developer web. Komponen ini berfungsi sebagai model yang baik dan menetapkan standar untuk membangun komponen gambar di framework lain, sistem pengelolaan konten (CMS), dan tech-stack. Kami telah berkolaborasi pada komponen serupa untuk Nuxt.js, dan kami sedang bekerja sama dengan Angular untuk pengoptimalan gambar di versi mendatang. Postingan ini membahas cara kami mendesain komponen Gambar Next.js dan pelajaran yang kami dapatkan selama prosesnya.
Masalah dan peluang pengoptimalan gambar
Gambar tidak hanya memengaruhi performa, tetapi juga bisnis. Jumlah gambar di halaman menjadi prediktor terbesar kedua untuk konversi pengguna yang mengunjungi situs. Sesi saat pengguna melakukan konversi memiliki 38% lebih sedikit gambar daripada sesi saat mereka tidak melakukan konversi. Lighthouse mencantumkan beberapa peluang untuk mengoptimalkan gambar dan meningkatkan data web sebagai bagian dari audit praktik terbaiknya. Beberapa area umum tempat gambar dapat memengaruhi data web inti dan pengalaman pengguna adalah sebagai berikut.
Gambar tanpa ukuran akan menurunkan CLS
Gambar yang ditayangkan tanpa ukuran yang ditentukan dapat menyebabkan ketidakstabilan tata letak dan berkontribusi pada Pergeseran Tata Letak Kumulatif (CLS) yang tinggi. Menetapkan atribut width
dan height
pada elemen img dapat membantu mencegah pergeseran tata letak. Contoh:
<img src="flower.jpg" width="360" height="240">
Lebar dan tinggi harus disetel sedemikian rupa sehingga rasio aspek gambar yang dirender mendekati rasio aspek naturalnya. Perbedaan rasio aspek yang signifikan dapat menyebabkan gambar terlihat terdistorsi. Properti yang relatif baru yang memungkinkan Anda menentukan rasio aspek di CSS dapat membantu menentukan ukuran gambar secara responsif sekaligus mencegah CLS.
Gambar berukuran besar dapat mengganggu LCP
Semakin besar ukuran file gambar, semakin lama waktu yang diperlukan untuk mendownloadnya. Gambar besar dapat berupa gambar "hero" untuk halaman atau elemen paling signifikan di area pandang yang bertanggung jawab untuk memicu Largest Contentful Paint (LCP). Gambar yang merupakan bagian dari konten penting dan memerlukan waktu lama untuk didownload akan menunda LCP.
Dalam banyak kasus, developer dapat mengurangi ukuran gambar melalui kompresi yang lebih baik dan penggunaan gambar yang responsif. Atribut srcset
dan sizes
dari elemen <img>
membantu menyediakan file gambar dengan ukuran yang berbeda. Browser kemudian dapat memilih yang tepat, bergantung pada ukuran dan resolusi layar.
Kompresi gambar yang buruk dapat menurunkan LCP
Format gambar modern seperti AVIF atau WebP dapat memberikan kompresi yang lebih baik daripada format JPEG dan PNG yang biasa digunakan. Dalam beberapa kasus, kompresi yang lebih baik mengurangi ukuran file sebesar 25% hingga 50% untuk kualitas gambar yang sama. Pengurangan ini menyebabkan download lebih cepat dengan lebih sedikit konsumsi data. Aplikasi harus menyalurkan format gambar modern ke browser yang mendukung format ini.
Memuat gambar yang tidak perlu akan menurunkan LCP
Gambar di bawah lipatan atau yang tidak ada di area pandang tidak akan ditampilkan kepada pengguna saat halaman dimuat. Hasil ini dapat ditangguhkan sehingga tidak berkontribusi pada LCP dan menundanya. Pemuatan lambat dapat digunakan untuk memuat gambar tersebut nanti saat pengguna men-scroll ke arahnya.
Tantangan pengoptimalan
Tim dapat mengevaluasi biaya performa karena masalah yang tercantum sebelumnya dan menerapkan solusi praktik terbaik untuk mengatasinya. Namun, hal ini sering kali tidak terjadi dalam praktiknya, dan gambar yang tidak efisien terus memperlambat web. Kemungkinan alasannya adalah:
- Prioritas: Developer web biasanya cenderung berfokus pada kode, JavaScript, dan pengoptimalan data. Dengan demikian, mereka mungkin tidak mengetahui masalah pada gambar atau cara mengoptimalkannya. Gambar yang dibuat oleh desainer atau diupload oleh pengguna mungkin tidak terlalu penting dalam daftar prioritas.
- Solusi siap pakai: Meskipun developer mengetahui nuansa optimalisasi gambar, tidak adanya solusi siap pakai serbaguna untuk framework atau stack teknologi mereka dapat menjadi penghalang.
- Gambar dinamis: Selain gambar statis yang merupakan bagian dari aplikasi, gambar dinamis diupload oleh pengguna atau bersumber dari database atau CMS eksternal. Mungkin sulit untuk menentukan ukuran gambar tersebut jika sumber gambar bersifat dinamis.
- Beban markup yang berlebihan: Solusi untuk menyertakan ukuran gambar atau
srcset
untuk ukuran yang berbeda memerlukan markup tambahan untuk setiap gambar, yang dapat merepotkan. Atributsrcset
diperkenalkan pada tahun 2014, tetapi hanya digunakan oleh 26,5% situs saat ini. Saat menggunakansrcset
, developer harus membuat gambar dalam berbagai ukuran. Alat seperti just-gimme-an-img dapat membantu, tetapi harus digunakan secara manual untuk setiap gambar. - Dukungan browser: Format gambar modern seperti AVIF dan WebP membuat file gambar yang lebih kecil, tetapi memerlukan penanganan khusus di browser yang tidak mendukungnya. Developer harus menggunakan strategi seperti negosiasi konten atau elemen
<picture
> agar gambar ditayangkan ke semua browser. - Kompleksitas pemuatan lambat: Ada beberapa teknik dan library yang tersedia untuk menerapkan pemuatan lambat untuk gambar di bawah lipatan. Memilih yang terbaik bisa menjadi tantangan. Developer mungkin juga tidak tahu jarak terbaik dari "lipatan" untuk memuat gambar yang ditangguhkan. Ukuran area pandang yang berbeda di perangkat dapat lebih mempersulit hal ini.
- Lanskap yang berubah: Saat browser mulai mendukung fitur HTML atau CSS baru untuk meningkatkan performa, developer mungkin kesulitan mengevaluasi setiap fitur tersebut. Misalnya, Chrome memperkenalkan fitur Prioritas Pengambilan sebagai Uji Coba Origin. Fitur ini dapat digunakan untuk meningkatkan prioritas gambar tertentu di halaman. Secara keseluruhan, developer akan lebih mudah jika peningkatan tersebut dievaluasi dan diterapkan di tingkat komponen.
Komponen gambar sebagai solusi
Peluang yang tersedia untuk mengoptimalkan gambar dan tantangan dalam menerapkannya satu per satu untuk setiap aplikasi mengarahkan kami pada ide komponen gambar. Komponen gambar dapat merangkum dan menerapkan praktik terbaik. Dengan mengganti elemen <img>
dengan komponen gambar, developer dapat mengatasi masalah performa gambar dengan lebih baik.
Selama setahun terakhir, kami telah bekerja sama dengan framework Next.js untuk mendesain dan menerapkan komponen Gambar mereka. Elemen ini dapat digunakan sebagai pengganti langsung untuk elemen <img>
yang ada di aplikasi Next.js sebagai berikut.
// Before with <img> element:
function Logo() {
return <img src="/logo.jpg" alt="logo" height="200" width="100" />
}
// After with image component:
import Image from 'next/image'
function Logo() {
return <Image src="/logo.jpg" alt="logo" height="200" width="100" />
}
Komponen ini mencoba mengatasi masalah terkait gambar secara umum melalui serangkaian fitur dan prinsip yang beragam. Alat ini juga menyertakan opsi yang memungkinkan developer menyesuaikannya untuk berbagai persyaratan gambar.
Perlindungan dari pergeseran tata letak
Seperti yang telah dibahas sebelumnya, gambar yang tidak berukuran menyebabkan pergeseran tata letak dan berkontribusi pada CLS. Saat menggunakan komponen Gambar Next.js, developer harus memberikan ukuran gambar menggunakan atribut width
dan height
untuk mencegah pergeseran tata letak. Jika ukurannya tidak diketahui, developer harus menentukan layout=fill
untuk menayangkan gambar tidak berukuran yang berada di dalam container berukuran. Atau, Anda dapat menggunakan impor gambar statis untuk mengambil ukuran gambar sebenarnya di hard drive pada waktu build dan menyertakannya dalam gambar.
// Image component with width and height specified
<Image src="/logo.jpg" alt="logo" height="200" width="100" />
// Image component with layout specified
<Image src="/hero.jpg" layout="fill" objectFit="cover" alt="hero" />
// Image component with image import
import Image from 'next/image'
import logo from './logo.png'
function Logo() {
return <Image src={logo} alt="logo" />
}
Karena developer tidak dapat menggunakan komponen Gambar tanpa ukuran, desain ini memastikan bahwa mereka akan meluangkan waktu untuk mempertimbangkan ukuran gambar dan mencegah pergeseran tata letak.
Memfasilitasi responsivitas
Agar gambar responsif di seluruh perangkat, developer harus menetapkan atribut srcset
dan sizes
di elemen <img>
. Kami ingin mengurangi upaya ini dengan komponen Gambar. Kami mendesain komponen Gambar Next.js untuk menetapkan nilai atribut hanya sekali per aplikasi. Kita menerapkannya ke semua instance komponen Gambar berdasarkan mode tata letak. Kami menemukan solusi tiga bagian:
- Properti
deviceSizes
: Properti ini dapat digunakan untuk mengonfigurasi titik henti sementara satu kali berdasarkan perangkat yang umum untuk basis pengguna aplikasi. Nilai default untuk titik henti sementara disertakan dalam file konfigurasi. - Properti
imageSizes
: Ini juga merupakan properti yang dapat dikonfigurasi yang digunakan untuk mendapatkan ukuran gambar yang sesuai dengan titik henti sementara ukuran perangkat. - Atribut
layout
di setiap gambar: Atribut ini digunakan untuk menunjukkan cara menggunakan propertideviceSizes
danimageSizes
untuk setiap gambar. Nilai yang didukung untuk mode tata letak adalahfixed
,fill
,intrinsic
, danresponsive
Saat gambar diminta dengan mode tata letak responsif atau isi, Next.js mengidentifikasi gambar yang akan ditayangkan berdasarkan ukuran perangkat yang meminta halaman dan menetapkan srcset
dan sizes
dalam gambar dengan tepat.
Perbandingan berikut menunjukkan cara menggunakan mode tata letak untuk mengontrol ukuran gambar di berbagai layar. Kami telah menggunakan gambar demo yang dibagikan dalam dokumen Next.js, yang dilihat di ponsel dan laptop standar.
Layar laptop | Layar ponsel |
---|---|
Tata letak = Intrinsik: Menskalakan ke bawah agar sesuai dengan lebar penampung pada area pandang yang lebih kecil. Tidak diperbesar melebihi ukuran intrinsik gambar pada area pandang yang lebih besar. Lebar penampung adalah 100% | |
Tata Letak = Diperbaiki: Gambar tidak responsif. Lebar dan tinggi tetap mirip dengan elemen ``, terlepas dari perangkat tempatnya dirender. | |
Tata Letak = Responsif: Menskalakan ke bawah atau ke atas bergantung pada lebar penampung di berbagai area pandang, dengan mempertahankan rasio aspek. | |
Tata Letak = Isi: Lebar dan tinggi diregangkan untuk mengisi penampung induk. (Lebar <div> induk ditetapkan ke 300*500 dalam contoh ini)
|
|
Menyediakan pemuatan lambat bawaan
Komponen Gambar menyediakan solusi muat lambat bawaan yang berperforma tinggi sebagai default. Saat menggunakan elemen <img>
, ada beberapa opsi untuk pemuatan lambat, tetapi semuanya memiliki kekurangan yang membuatnya sulit digunakan. Developer dapat mengadopsi salah satu pendekatan pemuatan lambat berikut:
- Tentukan atribut
loading
: Ini didukung di semua browser modern. - Gunakan Intersection Observer API: Membuat solusi pemuatan lambat kustom memerlukan upaya serta desain dan penerapan yang cermat. Developer mungkin tidak selalu memiliki waktu untuk hal ini.
- Impor library pihak ketiga untuk memuat gambar secara lambat: Mungkin diperlukan upaya tambahan untuk mengevaluasi dan mengintegrasikan library pihak ketiga yang sesuai untuk pemuatan lambat.
Di komponen Gambar Next.js, pemuatan ditetapkan ke "lazy"
secara default. Pemuatan lambat diimplementasikan menggunakan Intersection Observer, yang tersedia di sebagian besar browser modern. Developer tidak perlu melakukan tindakan tambahan apa pun untuk mengaktifkannya, tetapi mereka dapat menonaktifkannya jika diperlukan.
Memuat gambar penting secara otomatis
Sering kali, elemen LCP berupa gambar, dan gambar berukuran besar dapat menunda LCP. Sebaiknya pramuat gambar penting agar browser dapat menemukan gambar tersebut lebih cepat. Saat menggunakan elemen <img>
, petunjuk pramuat dapat disertakan di head HTML sebagai berikut.
<link rel="preload" as="image" href="important.png">
Komponen gambar yang dirancang dengan baik harus menawarkan cara untuk menyesuaikan urutan pemuatan gambar, terlepas dari framework yang digunakan. Dalam kasus komponen Gambar Next.js, developer dapat menunjukkan gambar yang merupakan kandidat yang baik untuk pramuat menggunakan atribut priority
dari komponen gambar.
<Image src="/hero.jpg" alt="hero" height="400" width="200" priority />
Penambahan atribut priority
akan menyederhanakan markup dan lebih mudah digunakan. Developer komponen gambar juga dapat mempelajari opsi untuk menerapkan heuristik guna mengotomatiskan pramuat untuk gambar di atas lipatan di halaman yang memenuhi kriteria tertentu.
Mendorong hosting gambar berperforma tinggi
CDN Gambar direkomendasikan untuk mengotomatiskan pengoptimalan gambar, dan juga mendukung format gambar modern seperti WebP dan AVIF. Komponen Gambar Next.js menggunakan CDN gambar secara default menggunakan arsitektur loader. Contoh berikut menunjukkan bahwa loader memungkinkan konfigurasi CDN di file konfigurasi Next.js.
module.exports = {
images: {
loader: 'imgix',
path: 'https://ImgApp/imgix.net',
},
}
Dengan konfigurasi ini, developer dapat menggunakan URL relatif di sumber gambar, dan framework akan menggabungkan URL relatif dengan jalur CDN untuk membuat URL absolut. CDN gambar populer seperti Imgix, Cloudinary, dan Akamai didukung. Arsitektur ini mendukung penggunaan penyedia cloud kustom dengan menerapkan fungsi loader
kustom untuk aplikasi.
Mendukung gambar yang dihosting sendiri
Mungkin ada situasi saat situs tidak dapat menggunakan CDN gambar. Dalam kasus tersebut, komponen gambar harus mendukung gambar yang dihosting sendiri. Komponen Gambar Next.js menggunakan pengoptimal gambar sebagai server gambar bawaan yang menyediakan API seperti CDN. Pengoptimal menggunakan Sharp untuk transformasi gambar produksi jika diinstal di server. Library ini adalah pilihan bagus bagi siapa saja yang ingin membangun pipeline pengoptimalan gambar mereka sendiri.
Mendukung pemuatan progresif
Pemuatan progresif adalah teknik yang digunakan untuk mempertahankan minat pengguna dengan menampilkan gambar placeholder yang biasanya memiliki kualitas yang jauh lebih rendah saat gambar yang sebenarnya dimuat. Hal itu meningkatkan kinerja yang dirasakan dan meningkatkan pengalaman pengguna. Fitur ini dapat digunakan bersama dengan pemuatan lambat untuk gambar paruh bawah atau untuk gambar paruh atas.
Komponen Gambar Next.js mendukung pemuatan progresif untuk gambar melalui properti placeholder. Ini dapat digunakan sebagai LQIP (Placeholder gambar berkualitas rendah) untuk menampilkan gambar berkualitas rendah atau buram saat gambar sebenarnya dimuat.
Dampak
Dengan semua pengoptimalan ini, kami telah melihat kesuksesan dengan komponen Gambar Next.js dalam produksi dan juga bekerja dengan stack teknologi lain pada komponen gambar serupa.
Saat Leboncoin memigrasikan frontend JavaScript lama mereka ke Next.js, mereka juga mengupgrade pipeline gambar untuk menggunakan komponen Gambar Next.js. Di halaman yang dimigrasikan dari <img>
ke next/image, LCP turun dari 2,4 detik menjadi 1,7 detik. Total byte gambar yang didownload untuk halaman turun dari 663 kB menjadi 326 kB (dengan ~100 kB byte gambar yang dimuat lambat).
Pelajaran yang Diperoleh
Siapa pun yang membuat aplikasi Next.js bisa mendapatkan manfaat dari penggunaan komponen Gambar Next.js untuk pengoptimalan. Namun, jika Anda ingin membuat abstraksi performa serupa untuk framework atau CMS lain, berikut beberapa pelajaran yang kami pelajari selama prosesnya yang dapat membantu Anda.
Katup pengaman dapat menyebabkan lebih banyak kerugian daripada manfaat
Dalam rilis awal komponen Gambar Next.js, kami menyediakan atribut unsized
yang memungkinkan developer mengabaikan persyaratan ukuran, dan menggunakan gambar dengan dimensi yang tidak ditentukan. Kami pikir hal ini diperlukan jika tidak mungkin mengetahui tinggi atau lebar gambar sebelumnya. Namun, kami melihat pengguna merekomendasikan atribut unsized
dalam masalah GitHub sebagai solusi menyeluruh untuk masalah terkait persyaratan ukuran, bahkan jika mereka dapat menyelesaikan masalah dengan cara yang tidak memperburuk CLS. Selanjutnya, kami tidak lagi menggunakan dan menghapus atribut unsized
.
Memisahkan hambatan yang berguna dari gangguan yang tidak berguna
Persyaratan untuk mengubah ukuran gambar adalah contoh "gesekan yang berguna". Hal ini membatasi penggunaan komponen, tetapi memberikan manfaat performa yang luar biasa sebagai gantinya. Pengguna akan siap menerima batasan tersebut jika mereka memiliki gambaran yang jelas tentang potensi manfaat performa. Oleh karena itu, sebaiknya jelaskan kompromi ini dalam dokumentasi dan materi lain yang dipublikasikan tentang komponen.
Namun, Anda dapat menemukan solusi untuk hambatan tersebut tanpa mengorbankan performa. Misalnya, selama pengembangan komponen Gambar Next.js, kami menerima keluhan bahwa mencari ukuran untuk gambar yang disimpan secara lokal itu menjengkelkan. Kami menambahkan impor gambar statis, yang menyederhanakan proses ini dengan mengambil dimensi untuk gambar lokal secara otomatis pada waktu build menggunakan plugin Babel.
Mencapai keseimbangan antara fitur praktis dan pengoptimalan performa
Jika komponen gambar Anda tidak melakukan apa pun selain menimbulkan "hambatan yang berguna" bagi penggunanya, developer cenderung tidak ingin menggunakannya. Kami mendapati bahwa meskipun fitur performa seperti ukuran gambar dan pembuatan nilai srcset
otomatis adalah yang paling penting. Fitur praktis yang ditujukan untuk developer seperti pemuatan lambat otomatis dan placeholder buram bawaan juga mendorong minat pada komponen Gambar Next.js.
Menetapkan roadmap untuk fitur guna mendorong adopsi
Membuat solusi yang berfungsi sempurna untuk semua situasi sangatlah sulit. Anda mungkin tergoda untuk mendesain sesuatu yang berfungsi dengan baik untuk 75% orang, lalu memberi tahu 25% lainnya bahwa "dalam kasus ini, komponen ini tidak cocok untuk Anda".
Dalam praktiknya, strategi ini ternyata bertentangan dengan sasaran Anda sebagai desainer komponen. Anda ingin developer mengadopsi komponen Anda untuk mendapatkan manfaat dari keunggulan performanya. Hal ini sulit dilakukan jika ada sekelompok pengguna yang tidak dapat bermigrasi dan merasa tertinggal dari percakapan. Mereka cenderung akan mengekspresikan kekecewaan, yang menyebabkan persepsi negatif yang memengaruhi adopsi.
Sebaiknya Anda memiliki roadmap untuk komponen yang mencakup semua kasus penggunaan yang wajar dalam jangka panjang. Sebaiknya jelaskan secara eksplisit dalam dokumentasi tentang apa yang tidak didukung dan alasannya untuk menetapkan ekspektasi tentang masalah yang ingin dipecahkan oleh komponen.
Kesimpulan
Penggunaan dan pengoptimalan gambar itu rumit. Developer harus menemukan keseimbangan antara performa dan kualitas gambar sekaligus memastikan pengalaman pengguna yang baik. Hal ini membuat pengoptimalan gambar menjadi upaya yang berbiaya tinggi dan berdampak tinggi.
Daripada setiap aplikasi harus membuat ulang roda setiap saat, kami membuat template praktik terbaik yang dapat digunakan developer, framework, dan stack teknologi lainnya sebagai referensi untuk penerapan mereka sendiri. Pengalaman ini memang akan terbukti berharga saat kami mendukung framework lain, pada komponen gambarnya.
Komponen Gambar Next.js telah berhasil meningkatkan hasil performa di aplikasi Next.js, sehingga meningkatkan pengalaman pengguna. Kami yakin bahwa ini adalah model yang bagus dan akan berfungsi dengan baik di ekosistem yang lebih luas, dan kami ingin mendengar dari developer yang ingin mengadopsi model ini dalam project mereka.