Strategi pemotongan webpack yang lebih baru di Next.js dan Gatsby meminimalkan kode duplikat untuk meningkatkan performa pemuatan halaman.
Chrome berkolaborasi dengan alat dan framework dalam ekosistem open source JavaScript. Sejumlah pengoptimalan yang lebih baru baru-baru ini ditambahkan untuk meningkatkan performa pemuatan Next.js dan Gatsby. Artikel ini membahas strategi pemotongan terperinci yang lebih baik yang kini dikirimkan secara default di kedua framework.
Pengantar
Seperti banyak framework web lainnya, Next.js dan Gatsby menggunakan webpack sebagai kerangka kerja inti
pemaket. webpack v3 diperkenalkan
CommonsChunkPlugin
agar dapat
modul {i>output<i} digunakan bersama di antara titik masuk yang berbeda dalam satu (atau beberapa) "{i>commons<i}" potongan (atau
potongan). Kode yang dibagikan dapat diunduh secara terpisah dan disimpan di {i>cache browser<i} sejak dini yang dapat
menghasilkan performa pemuatan yang lebih baik.
Pola ini menjadi populer dengan banyak kerangka kerja aplikasi satu halaman yang mengadopsi titik entri dan konfigurasi paket yang terlihat seperti ini:
Meskipun praktis, konsep pemaketan semua kode modul
bersama menjadi satu potongan memiliki
keterbatasan praktik. Modul yang tidak dibagikan di setiap titik entri dapat didownload untuk rute yang tidak menggunakannya
mengakibatkan lebih banyak kode yang
didownload daripada yang diperlukan. Misalnya, saat page1
dimuat
potongan common
akan memuat kode untuk moduleC
meskipun page1
tidak menggunakan moduleC
.
Oleh karena itu, bersama dengan beberapa yang lain, webpack v4 menghapus plugin dan menggantinya
satu: SplitChunksPlugin
.
Potongan yang Lebih Baik
Setelan default untuk SplitChunksPlugin
berfungsi dengan baik bagi sebagian besar pengguna. Beberapa potongan
potongan adalah
dibuat bergantung pada sejumlah kondisi
untuk mencegah pengambilan kode duplikat di beberapa rute.
Namun, banyak framework web yang menggunakan plugin ini masih mengikuti "single-common" pendekatan untuk potongan
pemisahan. Misalnya, Next.js akan menghasilkan paket commons
yang berisi modul apa pun yang
digunakan di lebih dari 50% halaman dan semua dependensi framework (react
, react-dom
, dan seterusnya).
const splitChunksConfigs = {
…
prod: {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
commons: {
name: 'commons',
chunks: 'all',
minChunks: totalPages > 2 ? totalPages * 0.5 : 2,
},
react: {
name: 'commons',
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|scheduler|use-subscription)[\\/]/,
},
},
},
Meskipun memasukkan kode yang bergantung pada kerangka kerja ke dalam potongan bersama berarti kode tersebut dapat diunduh dan meng-cache untuk titik entri mana pun, heuristik berbasis penggunaan menyertakan modul umum yang digunakan di lebih dari setengah halaman tidak terlalu efektif. Mengubah rasio ini hanya akan menghasilkan salah satu dari dua hasil:
- Jika Anda mengurangi rasio, akan ada lebih banyak kode yang tidak diperlukan yang akan didownload.
- Jika Anda meningkatkan rasionya, lebih banyak kode yang akan diduplikasi di beberapa rute.
Untuk mengatasi masalah ini, Next.js menggunakan berbagai
konfigurasi untuk SplitChunksPlugin
yang mengurangi
kode yang tidak diperlukan untuk rute apa pun.
- Modul pihak ketiga yang berukuran cukup besar (lebih dari 160 KB) akan dibagi menjadi modul tersendiri potongan
- Potongan
frameworks
terpisah dibuat untuk dependensi framework (react
,react-dom
, dan dan seterusnya) - Potongan bersama sebanyak yang diperlukan telah dibuat (hingga 25)
- Ukuran minimum untuk potongan yang akan dihasilkan diubah menjadi 20 KB
Strategi pemotongan terperinci ini memberikan manfaat sebagai berikut:
- Waktu muat halaman lebih cepat. Memberikan beberapa potongan bersama, bukan satu, meminimalkan jumlah kode yang tidak diperlukan (atau duplikat) untuk setiap titik masuk.
- Meningkatkan cache selama navigasi. Memisahkan library besar dan dependensi framework menjadi potongan terpisah akan mengurangi kemungkinan invalidasi {i>cache<i} karena keduanya cenderung tidak hingga perubahan dilakukan.
Anda dapat melihat seluruh konfigurasi yang digunakan Next.js di webpack-config.ts
.
Permintaan HTTP lainnya
SplitChunksPlugin
menentukan dasar pemotongan terperinci, dan menerapkan pendekatan ini ke
framework seperti Next.js bukanlah konsep yang sepenuhnya baru. Banyak {i>framework<i},
bagaimanapun, masih terus
menggunakan satu heuristik dan "{i>commons<i}" karena beberapa alasan. Termasuk kekhawatiran bahwa
terlalu banyak permintaan HTTP dapat berdampak negatif pada performa situs.
Browser hanya dapat membuka koneksi TCP dalam jumlah terbatas ke satu origin (6 untuk Chrome), jadi meminimalkan jumlah potongan yang dihasilkan oleh pemaket dapat memastikan bahwa jumlah total permintaan tetap berada di bawah batas ini. Namun, ini hanya berlaku untuk HTTP/1.1. Multiplexing di HTTP/2 memungkinkan beberapa permintaan di-streaming secara paralel menggunakan satu koneksi melalui satu tempat asal. Dengan kata lain, umumnya kita tidak perlu khawatir tentang membatasi jumlah potongan yang dikeluarkan oleh pemaket kita.
Semua browser utama mendukung HTTP/2. Tim Chrome dan Next.js
ingin melihat apakah meningkatkan jumlah permintaan dengan memisahkan satu "commons" Next.js paket
menjadi beberapa bagian bersama akan mempengaruhi
performa pemuatan dengan cara apa pun. Mereka memulai dengan mengukur
performa 1 situs sekaligus mengubah jumlah maksimum permintaan paralel menggunakan
maxInitialRequests
saat ini.
Dalam rata-rata tiga kali percobaan
pada satu laman web,
load
,
start-render
dan First Contentful Paint semua tetap sama saat memvariasikan nilai awal maksimum
jumlah permintaan (dari 5 hingga 15). Cukup menarik, kami melihat adanya
sedikit overhead kinerja
setelah membagi secara agresif menjadi ratusan permintaan.
Hasil ini menunjukkan bahwa tetap berada di bawah ambang batas yang dapat diandalkan (20~25 permintaan) mencapai keseimbangan yang tepat
antara performa pemuatan dan efisiensi caching. Setelah beberapa pengujian dasar, 25 dipilih sebagai
jumlah maxInitialRequest
.
Mengubah jumlah maksimum permintaan yang terjadi secara paralel menghasilkan lebih dari satu paket bersama, dan memisahkannya dengan tepat untuk setiap titik masuk mengurangi jumlah kode yang tidak diperlukan untuk halaman yang sama.
Eksperimen ini hanya bertujuan mengubah jumlah permintaan untuk melihat apakah
dampak negatif terhadap performa pemuatan halaman. Hasilnya menyarankan agar menyetel maxInitialRequests
ke
25
pada halaman pengujian sudah optimal karena mengurangi ukuran payload JavaScript tanpa melambat
ke bagian bawah halaman. Jumlah total JavaScript yang diperlukan untuk menghidrasi halaman tetap
hampir sama, yang menjelaskan mengapa kinerja pemuatan laman tidak serta merta meningkat dengan
jumlah kode.
webpack menggunakan 30 KB sebagai ukuran minimum default untuk potongan yang akan dihasilkan. Namun, menggabungkan
Nilai maxInitialRequests
sebesar 25 dengan ukuran minimum 20 KB menghasilkan caching yang lebih baik.
Pengurangan ukuran dengan potongan terperinci
Banyak framework, termasuk Next.js, mengandalkan perutean sisi klien (ditangani oleh JavaScript) untuk memasukkan tag skrip yang lebih baru untuk setiap transisi rute. Tetapi bagaimana mereka menentukan potongan dinamis ini terlebih dahulu pada waktu build?
Next.js menggunakan file manifes build sisi server untuk menentukan potongan output mana yang digunakan oleh titik masuk yang berbeda. Untuk memberikan informasi ini kepada klien juga, ringkasan sisi klien file manifes build dibuat untuk memetakan semua dependensi untuk setiap titik entri.
// Returns a promise for the dependencies for a particular route
getDependencies (route) {
return this.promisedBuildManifest.then(
man => (man[route] && man[route].map(url => `/_next/${url}`)) || []
)
}
Strategi pemotongan terperinci yang lebih baru ini pertama kali diluncurkan di Next.js di belakang flag, tempat ia diuji pada jumlah pengguna awal. Banyak yang melihat pengurangan yang signifikan terhadap total JavaScript yang digunakan untuk situs secara keseluruhan:
Situs | Perubahan JS Total | % Perbedaan |
---|---|---|
https://www.barnebys.com/ | -238 KB | -23% |
https://sumup.com/ | -220 KB | -30% |
https://www.hashicorp.com/ | -11 MB | -71% |
Versi final dikirimkan secara default dalam versi 9.2.
Gatsby
Gatsby dulunya mengikuti pendekatan yang sama, yakni menggunakan model heuristik untuk menentukan modul umum:
config.optimization = {
…
splitChunks: {
name: false,
chunks: `all`,
cacheGroups: {
default: false,
vendors: false,
commons: {
name: `commons`,
chunks: `all`,
// if a chunk is used more than half the components count,
// we can assume it's pretty global
minChunks: componentsCount > 2 ? componentsCount * 0.5 : 2,
},
react: {
name: `commons`,
chunks: `all`,
test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
},
Dengan mengoptimalkan konfigurasi webpack mereka untuk mengadopsi strategi pemotongan terperinci yang serupa, mereka juga melihat pengurangan JavaScript yang cukup besar di banyak situs besar:
Situs | Perubahan JS Total | % Perbedaan |
---|---|---|
https://www.gatsbyjs.org/ | -680 KB | -22% |
https://www.thirdandgrove.com/ | -390 KB | -25% |
https://ghost.org/ | -1,1 MB | -35% |
https://reactjs.org/ | -80 Kb | -8% |
Lihat PR untuk memahami bagaimana mereka mengimplementasikan logika ini ke dalam konfigurasi webpack mereka, yang dikirimkan secara {i>default<i} di v2.20.7.
Kesimpulan
Konsep pengiriman potongan terperinci tidak spesifik untuk Next.js, Gatsby, atau bahkan webpack. Siapa saja sebaiknya mempertimbangkan untuk meningkatkan strategi pengelompokan aplikasi jika mengikuti “keadaan umum” yang besar apa pun, apa pun framework atau pemaket modul yang digunakan.
- Jika Anda ingin melihat pengoptimalan pemotongan yang sama yang diterapkan pada aplikasi vanilla React, lihat contoh React ini aplikasi. Proses ini menggunakan versi sederhana dari strategi pemotongan yang terperinci dan dapat membantu Anda mulai menerapkan semacam logika tertentu ke situs Anda.
- Untuk Rollup, potongan dibuat secara terperinci secara default. Lihat
manualChunks
jika Anda ingin secara manual mengonfigurasi perilaku.