Pertanyaan Bagaimana memaksa browser untuk memuat ulang file cache CSS / JS?


Saya telah memperhatikan bahwa beberapa browser (khususnya, Firefox dan Opera) sangat bersemangat dalam menggunakan salinan cache .css dan .js file, bahkan di antara sesi browser. Ini menyebabkan masalah ketika Anda memperbarui salah satu file ini tetapi browser pengguna terus menggunakan salinan cache.

Pertanyaannya adalah: apa cara paling elegan untuk memaksa browser pengguna untuk memuat ulang file ketika telah berubah?

Idealnya solusi tidak akan memaksa browser untuk memuat ulang file pada setiap kunjungan ke halaman. Saya akan posting solusi saya sendiri sebagai jawaban, tapi saya ingin tahu apakah ada yang memiliki solusi yang lebih baik dan saya akan membiarkan suara Anda memutuskan.

Memperbarui:

Setelah membolehkan diskusi di sini untuk sementara waktu, saya telah menemukan John Millikin dan da5idsaran untuk menjadi berguna. Ternyata ada istilah untuk ini: versi otomatis.

Saya telah memposting jawaban baru di bawah ini yang merupakan kombinasi dari solusi asli saya dan saran John.

Ide lain yang disarankan oleh SCdF akan menambahkan string kueri palsu ke file. (Beberapa kode Python untuk secara otomatis menggunakan stempel waktu sebagai string kueri palsu dikirim oleh pi.). Namun, ada beberapa diskusi mengenai apakah browser akan menyimpan file dengan string kueri atau tidak. (Ingat, kami ingin browser untuk menyimpan file dan menggunakannya pada kunjungan mendatang. Kami hanya ingin mengambil file lagi ketika telah diubah.)

Karena tidak jelas apa yang terjadi dengan string kueri palsu, saya tidak menerima jawaban itu.


865
2017-09-23 03:07


asal


Jawaban:


Memperbarui:  Ditulis ulang untuk memasukkan saran dari John Millikin dan da5id. Solusi ini ditulis dalam PHP, tetapi harus mudah disesuaikan dengan bahasa lain.

Perbarui 2: Memasukkan komentar dari Nick Johnson yang asli .htaccess regex dapat menyebabkan masalah dengan file seperti json-1.3.js. Solusinya adalah hanya menulis ulang jika ada tepat 10 digit di bagian akhir. (Karena 10 digit mencakup semua cap waktu dari 9/9/2001 hingga 11/20/2286.)

Pertama, kami menggunakan aturan penulisan ulang berikut di .htaccess:

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

Sekarang, kita menulis fungsi PHP berikut:

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *  
 *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
 *                starting with slash).
 */
function auto_version($file)
{
  if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
    return $file;

  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}

Sekarang, di mana pun Anda memasukkan CSS, ubah dari ini:

<link rel="stylesheet" href="/css/base.css" type="text/css" />

Untuk ini:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />

Dengan cara ini, Anda tidak perlu mengubah tag tautan lagi, dan pengguna akan selalu melihat CSS terbaru. Browser akan dapat menyimpan cache file CSS, tetapi ketika Anda melakukan perubahan pada CSS Anda, browser akan melihat ini sebagai URL baru, sehingga tidak akan menggunakan salinan cache.

Ini juga dapat berfungsi dengan gambar, favicons, dan JavaScript. Pada dasarnya apa pun yang tidak dihasilkan secara dinamis.


415
2017-09-23 04:04



Teknik Sisi Klien Sederhana

Secara umum, cache bagus. Jadi ada beberapa teknik, tergantung pada apakah Anda memperbaiki masalah untuk diri sendiri saat Anda mengembangkan situs web, atau apakah Anda mencoba untuk mengontrol cache di lingkungan produksi.

Pengunjung umum ke situs web Anda tidak akan memiliki pengalaman yang sama dengan yang Anda alami saat mengembangkan situs. Karena pengunjung rata-rata datang ke situs lebih jarang (mungkin hanya beberapa kali setiap bulan, kecuali Anda adalah Google atau hi5 Networks), maka mereka cenderung memiliki file Anda dalam cache, dan itu mungkin cukup. Jika Anda ingin memaksa versi baru ke peramban, Anda selalu dapat menambahkan string kueri ke permintaan, dan menaikkan nomor versi ketika Anda membuat perubahan besar:

<script src="/myJavascript.js?version=4"></script>

Ini akan memastikan bahwa setiap orang mendapat file baru. Ini berfungsi karena browser melihat URL file untuk menentukan apakah ia memiliki salinan dalam cache. Jika server Anda tidak diatur untuk melakukan apa pun dengan string kueri, itu akan diabaikan, tetapi nama akan terlihat seperti file baru ke browser.

Di sisi lain, jika Anda mengembangkan situs web, Anda tidak ingin mengubah nomor versi setiap kali Anda menyimpan perubahan ke versi pengembangan Anda. Itu akan membosankan.

Jadi ketika Anda mengembangkan situs Anda, trik yang baik adalah secara otomatis menghasilkan parameter string kueri:

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>

Menambahkan string kueri ke permintaan adalah cara yang bagus untuk memversi sumber daya, tetapi untuk situs web sederhana ini mungkin tidak diperlukan. Dan ingat, caching adalah hal yang baik.

Ini juga perlu dicatat bahwa browser tidak selalu pelit tentang menyimpan file dalam cache. Browser memiliki kebijakan untuk hal semacam ini, dan mereka biasanya bermain dengan aturan yang ditetapkan dalam spesifikasi HTTP. Ketika browser membuat permintaan ke server, bagian dari respons adalah header EXPIRES .. tanggal yang memberitahukan browser berapa lama harus disimpan dalam cache. Saat berikutnya browser menemukan permintaan untuk file yang sama, ia melihat bahwa ia memiliki salinan dalam cache dan melihat tanggal EXPIRES untuk memutuskan apakah itu harus digunakan.

Jadi percaya atau tidak, sebenarnya server Anda yang membuat cache browser begitu persisten. Anda dapat menyesuaikan pengaturan server Anda dan mengubah header EXPIRES, tetapi teknik kecil yang saya tulis di atas mungkin merupakan cara yang jauh lebih mudah bagi Anda untuk melakukannya. Karena caching itu baik, Anda biasanya ingin menetapkan tanggal itu jauh ke masa depan ("Header Kedaluwarsa Masa Depan"), dan menggunakan teknik yang dijelaskan di atas untuk memaksa perubahan.

Jika Anda tertarik dengan informasi lebih lanjut tentang HTTP atau bagaimana permintaan ini dibuat, buku yang bagus adalah "High Performance Web Sites" oleh Steve Souders. Ini pengantar yang sangat bagus untuk subjek.


162
2017-09-23 13:25



Milik Google mod_pagespeed plugin untuk apache akan melakukan auto-versioning untuk Anda. Ini sangat licin.

Ini mem-parsing HTML di jalan keluar dari webserver (bekerja dengan PHP, rel, python, HTML statis - apa saja) dan menulis ulang tautan ke CSS, JS, file gambar sehingga mereka menyertakan kode id. Ini berfungsi file di URL yang dimodifikasi dengan kontrol cache yang sangat panjang pada mereka. Ketika file berubah, secara otomatis mengubah URL sehingga browser harus mengambilnya kembali. Pada dasarnya ini berfungsi, tanpa ada perubahan pada kode Anda. Itu bahkan akan mengecilkan kode Anda di jalan keluar juga.


110
2017-09-23 03:12



Alih-alih mengubah versi secara manual, saya sarankan Anda menggunakan MD5 hash dari file CSS yang sebenarnya.

Jadi URL Anda akan menjadi sesuatu seperti

http://mysite.com/css/[md5_hash_here]/style.css

Anda masih bisa menggunakan aturan penulisan ulang untuk menghapus hash, tetapi keuntungannya adalah sekarang Anda dapat mengatur kebijakan cache Anda menjadi "cache selamanya", karena jika URLnya sama, itu berarti bahwa file tersebut tidak berubah.

Anda kemudian dapat menulis skrip shell sederhana yang akan menghitung hash file dan memperbarui tag Anda (Anda mungkin ingin memindahkannya ke file terpisah untuk dimasukkan).

Cukup jalankan skrip itu setiap kali CSS berubah dan Anda bagus. Browser HANYA akan memuat ulang file Anda ketika diubah. Jika Anda melakukan pengeditan dan kemudian membatalkannya, tidak ada rasa sakit dalam mencari tahu versi mana yang perlu Anda gunakan kembali agar pengunjung tidak mengunduh ulang.


90
2017-09-23 03:21



Tidak yakin mengapa Anda begitu menderita untuk menerapkan solusi ini.

Yang perlu Anda lakukan jika mendapatkan cap waktu dimodifikasi file dan menambahkannya sebagai querystring ke file

Di PHP saya akan melakukannya seperti:

<link rel="stylesheet" href="mycss.css?v=<?php echo filemtime('mycss.css') ?>"/>

filemtime adalah fungsi PHP yang mengembalikan cap waktu file yang dimodifikasi.


57
2017-09-23 06:02



Anda cukup meletakkannya ?foo=1234 pada akhir impor css / js Anda, ubah 1234 menjadi apa pun yang Anda suka. Silahkan lihat sumber SO html untuk contoh.

Gagasan di sana adalah bahwa? parameter dibuang / diabaikan pada permintaan dan Anda dapat mengubah nomor itu ketika Anda meluncurkan versi baru.


catatan: Ada beberapa argumen yang berkaitan dengan bagaimana hal ini mempengaruhi caching. Saya percaya intinya adalah bahwa GET meminta, dengan atau tanpa parameter harus dapat di-cachable, sehingga solusi di atas seharusnya berfungsi.

Namun, baik bagi server web untuk memutuskan apakah ingin mematuhi bagian spesifikasi itu dan browser yang digunakan pengguna, karena hanya dapat pergi ke depan dan meminta versi baru pula.


50
2017-08-05 16:55



Saya pernah mendengar ini disebut "versi otomatis". Metode yang paling umum adalah menyertakan mime file statis di suatu tempat di URL, dan menghapusnya menggunakan penangan penulisan ulang atau URL confs:

Lihat juga:


37
2018-06-24 23:20



30 atau lebih jawaban yang ada adalah saran yang bagus untuk situs web tahun 2008. Namun, ketika menyangkut modern, aplikasi satu halaman (SPA), mungkin sudah waktunya untuk berpikir ulang beberapa asumsi mendasar ... khususnya gagasan bahwa diinginkan untuk server web hanya melayani versi tunggal terbaru dari sebuah file.

Bayangkan Anda adalah pengguna yang memiliki versi M dari SPA dimuat ke browser Anda:

  1. Pipa CD Anda menyebarkan versi baru N aplikasi ke server
  2. Anda menavigasi dalam SPA, yang mengirim XHR ke server untuk mendapatkannya /some.template
    • (Browser Anda belum me-refresh halaman, jadi Anda masih menjalankan versi M)
  3. Server merespons dengan konten /some.template - apakah Anda ingin mengembalikan versi M atau N dari template?

Jika format /some.template berubah antara versi M dan N (atau file diganti namanya atau apa pun) Anda mungkin tidak ingin versi N dari template yang dikirim ke browser yang menjalankan versi lama M dari parser. †

Aplikasi web mengalami masalah ini ketika dua ketentuan dipenuhi:

  • Sumber daya diminta secara asinkron beberapa saat setelah pemuatan halaman awal
  • Logika aplikasi mengasumsikan hal-hal (yang dapat berubah di versi masa depan) tentang konten sumber daya

Setelah aplikasi Anda perlu menyajikan beberapa versi secara paralel, memecahkan caching dan "reload" menjadi sepele:

  1. Instal semua file situs ke dir versi berversi: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
  2. Setel header HTTP untuk membiarkan browser menyimpan file selamanya
    • (Atau lebih baik lagi, masukkan semuanya ke CDN)
  3. Perbarui semua <script> dan <link> tag, dll. untuk menunjuk ke file itu di salah satu dir versi

Langkah terakhir itu terdengar rumit, karena membutuhkan pemanggilan URL untuk setiap URL dalam kode sisi-server atau sisi-klien Anda. Atau Anda hanya bisa memanfaatkannya secara pintar <base> menandai dan ubah versi saat ini di satu tempat.

† Salah satu cara untuk mengatasi hal ini adalah dengan agresif memaksa browser untuk memuat ulang semuanya ketika versi baru dirilis. Tetapi demi membiarkan operasi yang sedang berlangsung untuk diselesaikan, mungkin masih paling mudah untuk mendukung setidaknya dua versi secara paralel: v-current dan v-previous.


19
2017-09-23 03:23



Jangan gunakan foo.css? Version = 1! Browser tidak seharusnya meng-cache URL dengan variabel GET. Menurut http://www.thinkvitamin.com/features/webapps/serving-javascript-fast, meskipun IE dan Firefox mengabaikan ini, Opera dan Safari tidak! Sebagai gantinya, gunakan foo.v1234.css, dan gunakan aturan penulisan ulang untuk menghapus nomor versi.


14
2017-09-23 13:54



The RewriteRule membutuhkan pembaruan kecil untuk file js atau css yang berisi versi notasi titik di bagian akhir. Misalnya. json-1.3.js.

Saya menambahkan kelas negasi dot [^.] Ke regex, jadi. diabaikan.

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]

9
2017-09-24 11:38



Untuk ASP.NET 4.5 dan lebih besar dapat Anda gunakan bundling skrip.

Permintaan http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81 adalah untuk bundel AllMyScripts dan berisi pasangan string kueri v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. String kueri v memiliki token nilai yang merupakan pengenal unik yang digunakan untuk caching. Selama bundel tidak berubah, aplikasi ASP.NET akan meminta bundel AllMyScripts menggunakan token ini. Jika ada file dalam bundel berubah, kerangka kerja optimasi ASP.NET akan menghasilkan token baru, menjamin bahwa permintaan browser untuk bundel akan mendapatkan bundel terbaru.

Ada manfaat lain untuk bundling termasuk peningkatan kinerja pada pemuatan halaman pertama kali dengan minification.


9