Selama beberapa tahun sekarang, karena hampir setiap artikel tentang pendekatan lanjutan untuk caching merekomendasikan penggunaan teknik berikut dalam produksi:
- Menambahkan informasi nama file tentang versi data yang terkandung di dalamnya (biasanya dalam bentuk hash data dalam file).
- Menyetel tajuk HTTP
Cache-Control: max-age
dan Expires
, yang mengontrol waktu caching materi (yang menghilangkan validasi ulang materi yang relevan bagi pengunjung yang kembali ke sumber daya).

Semua alat untuk membangun proyek yang saya tahu mendukung menambahkan ke file hash nama isinya. Ini dilakukan dengan menggunakan aturan konfigurasi sederhana (seperti yang ditunjukkan di bawah):
filename: '[name]-[contenthash].js'
Dukungan luas seperti ini untuk teknologi ini telah mengarah pada fakta bahwa praktik ini telah menjadi sangat umum.
Pakar kinerja proyek web juga merekomendasikan menggunakan teknik
pemisahan kode . Teknik-teknik ini memungkinkan memecah kode JavaScript menjadi bundel terpisah. Kumpulan tersebut dapat diunduh oleh browser secara paralel, atau bahkan hanya jika diperlukan, atas permintaan browser.
Salah satu dari banyak keuntungan pemisahan kode, khususnya, terkait dengan teknik caching terbaik, adalah bahwa perubahan yang dilakukan pada file terpisah dengan kode sumber tidak menyebabkan pembatalan cache seluruh bundel. Dengan kata lain, jika pembaruan keamanan dirilis untuk paket npm yang dibuat oleh pengembang "X", dan konten
node_modules
difragmentasi oleh pengembang, maka hanya fragmen yang berisi paket yang dibuat oleh "X" yang harus diubah.
Masalahnya di sini adalah jika semua ini digabungkan, maka ini jarang mengarah pada peningkatan efisiensi caching data jangka panjang.
Dalam praktiknya, perubahan pada salah satu file kode sumber hampir selalu mengakibatkan pembatalan lebih dari satu file output dari sistem paket rakitan. Dan ini justru karena fakta bahwa hash telah ditambahkan ke nama file yang mencerminkan versi isi file-file ini.
Masalah Versi Nama File
Bayangkan Anda telah membuat dan menggunakan situs web. Anda telah menggunakan pemisahan kode, sebagai akibatnya, sebagian besar kode JavaScript situs Anda dimuat berdasarkan permintaan.
Di diagram dependensi berikutnya, Anda dapat melihat titik entri basis kode - fragmen root
main
, serta tiga fragmen dependensi yang dimuat secara tidak sinkron -
dep1
,
dep3
dan
dep3
. Ada juga fragmen
vendor
berisi semua dependensi situs dari
node_modules
. Semua nama file, sesuai dengan pedoman caching, termasuk hash dari isi file-file ini.
Pohon dependensi modul JavaScript tipikalKarena
dep3
dan
dep3
mengimpor modul dari fragmen
vendor
, maka di bagian atas kode mereka dihasilkan oleh
dep3
proyek, kami kemungkinan besar akan menemukan perintah impor yang terlihat seperti ini:
import {...} from '/vendor-5e6f.mjs';
Sekarang mari kita pikirkan tentang apa yang akan terjadi jika isi dari fragmen
vendor
berubah.
Jika ini terjadi, hash dalam nama file yang sesuai juga akan berubah. Dan karena tautan ke nama file ini ada di perintah impor untuk
dep3
dan
dep3
, maka perintah-perintah impor ini perlu diubah:
-import {...} from '/vendor-5e6f.mjs'; +import {...} from '/vendor-d4a1.mjs';
Namun, karena perintah impor ini adalah bagian dari isi dari
dep3
dan
dep3
, mengubahnya berarti bahwa hash dari isi file
dep3
dan
dep3
juga
dep2
dep3
. Dan itu berarti bahwa nama-nama file ini juga akan berubah.
Tetapi ini tidak berakhir di sana. Karena fragmen
main
mengimpor fragmen
dep3
dan
dep3
, dan nama file mereka telah berubah, perintah impor di
main
juga akan berubah:
-import {...} from '/dep2-3c4d.mjs'; +import {...} from '/dep2-2be5.mjs'; -import {...} from '/dep3-d4e5.mjs'; +import {...} from '/dep3-3c6f.mjs';
Dan akhirnya, karena isi file
main
telah berubah, nama file ini juga harus diubah.
Beginilah diagram dependensi sekarang akan terlihat.
Modul di pohon ketergantungan dipengaruhi oleh perubahan tunggal dalam kode salah satu simpul daun pohonContoh ini menunjukkan bagaimana perubahan kode kecil yang dibuat hanya dalam satu file menyebabkan pembatalan cache 80% dari fragmen bundel.
Meskipun memang benar bahwa tidak semua perubahan menyebabkan konsekuensi yang menyedihkan (misalnya, membatalkan cache node daun menyebabkan validasi cache semua node hingga ke root, tetapi membatalkan cache root tidak menyebabkan cascading invalidation mencapai tangkapan daun), di dunia yang ideal kami tidak perlu berurusan dengan pembatalan cache yang tidak perlu.
Ini membawa kita pada pertanyaan berikut: "Apakah mungkin untuk mendapatkan manfaat dari sumber daya yang tidak dapat diubah dan caching jangka panjang, sementara tidak menderita cascading cache invalidations?"
Pendekatan Pemecahan Masalah
Masalah dengan hash dari isi file dalam nama file, dari sudut pandang teknis, bukanlah bahwa hash ada dalam nama. Itu terletak pada fakta bahwa hash ini muncul di dalam file lain. Akibatnya, cache dari file-file ini dinonaktifkan ketika mengubah hash dalam nama-nama file di mana mereka bergantung.
Solusi untuk masalah ini adalah dengan menggunakan bahasa contoh di atas untuk memungkinkan untuk mengimpor fragmen
vendor
oleh
dep3
dan
dep3
tanpa menentukan informasi versi file fragmen
vendor
. Dengan demikian, Anda harus memastikan bahwa versi
vendor
diunduh sudah benar, dengan mempertimbangkan versi
dep3
dan
dep3
.
Ternyata, ada beberapa cara untuk mencapai tujuan ini:
- Kartu impor.
- Pekerja Layanan.
- Skrip asli untuk memuat sumber daya.
Pertimbangkan mekanisme ini.
Pendekatan # 1: Impor Kartu
Mengimpor peta adalah solusi paling sederhana untuk cascading invalidation cache. Selain itu, mekanisme ini paling mudah diterapkan. Tetapi, sayangnya, hanya didukung di Chrome (fitur ini, terlebih lagi, harus
diaktifkan secara eksplisit).
Meskipun demikian, saya ingin memulai dengan cerita tentang kartu impor, karena saya yakin keputusan ini akan menjadi yang paling umum di masa depan. Selain itu, deskripsi bekerja dengan kartu impor akan membantu menjelaskan fitur-fitur pendekatan lain untuk menyelesaikan masalah kita.
Menggunakan peta impor untuk mencegah cascading invalidation terdiri dari tiga langkah.
โLangkah 1
Anda perlu mengkonfigurasi bundler sehingga ketika membangun proyek itu tidak termasuk hash dari isi file dalam nama mereka.
Jika Anda merakit proyek yang modulnya diperlihatkan dalam diagram dari contoh sebelumnya, tanpa menyertakan hash dari isinya dalam nama file, file-file dalam direktori output proyek akan terlihat seperti ini:
dep1.mjs dep2.mjs dep3.mjs main.mjs vendor.mjs
Perintah impor dalam modul yang sesuai juga tidak akan menyertakan hash:
import {...} from '/vendor.mjs';
โLangkah 2
Anda perlu menggunakan alat seperti
rev-hash , dan menggunakannya untuk menghasilkan salinan setiap file dengan hash yang ditambahkan ke namanya yang menunjukkan versi isinya.
Setelah bagian pekerjaan ini selesai, isi direktori keluaran akan terlihat seperti yang ditunjukkan di bawah ini (perhatikan bahwa sekarang ada dua opsi untuk setiap file):
dep1-b2c3.mjs", dep1.mjs dep2-3c4d.mjs", dep2.mjs dep3-d4e5.mjs", dep3.mjs main-1a2b.mjs", main.mjs vendor-5e6f.mjs", vendor.mjs
โLangkah 3
Anda perlu membuat objek JSON yang menyimpan informasi tentang korespondensi setiap file yang namanya tidak ada hash untuk setiap file yang namanya hash. Objek ini perlu ditambahkan ke templat HTML.
Objek JSON ini adalah peta impor. Begini tampilannya:
<script type="importmap"> { "imports": { "/main.mjs": "/main-1a2b.mjs", "/dep1.mjs": "/dep1-b2c3.mjs", "/dep2.mjs": "/dep2-3c4d.mjs", "/dep3.mjs": "/dep3-d4e5.mjs", "/vendor.mjs": "/vendor-5e6f.mjs", } } </script>
Setelah itu, setiap kali browser melihat perintah impor file yang terletak di alamat yang sesuai dengan salah satu kunci peta impor, browser akan mengimpor file yang cocok dengan nilai kunci.
Jika Anda menggunakan peta impor ini sebagai contoh, Anda dapat mengetahui bahwa perintah impor yang mereferensikan file
/vendor.mjs
sebenarnya akan meminta dan memuat file
/vendor-5e6f.mjs
:
Ini berarti bahwa kode sumber modul dapat dengan mudah merujuk pada nama file dari modul yang tidak mengandung hash, dan browser akan selalu mengunduh file yang namanya berisi informasi tentang versi kontennya. Dan, karena tidak ada hash dalam kode sumber modul (mereka hanya hadir di peta impor), perubahan hash ini tidak akan menyebabkan pembatalan modul selain dari mereka yang isinya telah benar-benar berubah.
Mungkin Anda sekarang bertanya-tanya mengapa saya membuat salinan setiap file alih-alih hanya mengganti nama file. Ini diperlukan untuk mendukung browser yang tidak dapat berfungsi dengan impor peta. Pada contoh sebelumnya, browser semacam itu hanya akan melihat file
/vendor.mjs
dan cukup mengunduh file ini, melakukan seperti biasanya, menemui konstruksi yang serupa. Akibatnya, ternyata kedua file tersebut harus ada di server.
Jika Anda ingin melihat impor peta dalam aksi, berikut adalah
sekumpulan contoh yang menunjukkan semua cara untuk menyelesaikan masalah pembatalan cache cascading yang ditunjukkan pada artikel ini. Juga, lihat
konfigurasi perakitan proyek , jika Anda tertarik mempelajari bagaimana saya membuat peta impor dan hash versi untuk setiap file.
Dilanjutkan ...
Pembaca yang budiman! Apakah Anda mengetahui cascading invalidation cache?
