Bagaimana cara memaksa browser untuk memuat ulang file CSS / JS yang di-cache?

993

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

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

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

Perbarui:

Setelah membiarkan diskusi di sini untuk sementara waktu, saya telah menemukan saran John Millikin dan da5id 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.

Gagasan lain yang disarankan oleh SCdF adalah 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 tentang apakah browser akan men-cache file dengan string kueri. (Ingat, kami ingin browser untuk men-cache file dan menggunakannya pada kunjungan berikutnya. Kami hanya ingin mengambil file itu lagi ketika sudah berubah.)

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

Pmpr
sumber
Saya memiliki ini dalam .htaccess saya, dan tidak pernah ada masalah dengan file cache: ExpiresActive On ExpiresDefault "modification".
Frank Conijn
2
Saya pasti setuju bahwa menambahkan info versi ke URL file sejauh ini merupakan cara terbaik. Ini bekerja, setiap saat, untuk semua orang. Tapi, jika Anda tidak menggunakannya, dan Anda hanya perlu memuat ulang satu file CSS atau JS itu sesekali di browser Anda sendiri ... buka saja di tabnya sendiri dan tekan SHIFT-reload (atau CTRL-F5)! Anda dapat melakukan hal yang sama secara efektif menggunakan JS dengan memuat file dalam iframe (tersembunyi), menunggu hingga dimuat, dan kemudian memanggil iframe.contentWindow.location.reload(true). Lihat metode (4) dari stackoverflow.com/a/22429796/999120 - itu tentang gambar, tetapi hal yang sama berlaku.
Doin
2
Saya sangat menghargai cara pertanyaan ini diajukan dan telah diperbarui sejak saat itu. Itu benar-benar menggambarkan apa yang harus saya harapkan dalam jawaban. Saya akan mengikuti pendekatan ini dalam pertanyaan saya mulai sekarang. Bersulang!
rd22

Jawaban:

455

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

Pembaruan 2: Memasukkan komentar dari Nick Johnson bahwa .htaccessregex asli dapat menyebabkan masalah dengan file seperti json-1.3.js. Solusi adalah dengan hanya menulis ulang jika ada tepat 10 digit di 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 Anda, ubahlah 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 melakukan cache file CSS, tetapi ketika Anda membuat perubahan pada CSS Anda browser akan melihat ini sebagai URL baru, sehingga tidak akan menggunakan salinan cache.

Ini juga dapat bekerja dengan gambar, favicon, dan JavaScript. Pada dasarnya segala sesuatu yang tidak dihasilkan secara dinamis.

Tidur
sumber
16
Server konten statis saya sendiri melakukan hal yang persis sama, kecuali saya menggunakan parameter untuk versi (base.css? V = 1221534296) daripada perubahan nama file (base.1221534296.css). Saya curiga cara Anda mungkin sedikit lebih efisien. Sangat keren.
Jens Roland
4
@ Tip: Solusi yang sangat apik. Penulisan ulang URL jelas memiliki lebih banyak untuk ditawarkan daripada hanya url prettyfying.
James P.
37
Saya melihat masalah dengan ini, bahwa ia mengakses sistem file berkali-kali - tepatnya - jumlah tautan * jumlah permintaan / detik ... yang mungkin atau mungkin tidak menjadi masalah bagi Anda.
Tomáš Fejfar
3
@AlixAxel: Tidak, browser akan mengambilnya kembali ketika parameter berubah, tetapi beberapa proxy publik tidak akan menembolokkan file dengan parameter url, jadi praktik terbaik adalah memasukkan versi di path. Dan overhead mod_rewrite sangat kecil dibandingkan dengan setiap bottleneck kinerja lainnya di WPO
Jens Roland
8
Apakah file_existscek pertama benar-benar diperlukan? filemtimeakan mengembalikan false pada kegagalan, jadi mengapa tidak hanya menetapkan nilai filemtime ke variabel dan memeriksa apakah itu salah sebelum mengganti nama file? Itu akan mengurangi satu operasi file yang tidak perlu yang benar-benar akan bertambah.
Gavin
184

Teknik Sisi Klien Sederhana

Secara umum, caching itu baik. Jadi ada beberapa teknik, tergantung pada apakah Anda memperbaiki masalah sendiri saat Anda mengembangkan situs web, atau apakah Anda mencoba mengendalikan cache di lingkungan produksi.

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

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

Ini akan memastikan bahwa semua orang mendapatkan 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 untuk 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 saat Anda mengembangkan situs Anda, trik yang bagus adalah menghasilkan parameter string kueri secara otomatis:

<!-- 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 baik untuk versi sumber daya, tetapi untuk situs web sederhana ini mungkin tidak perlu. Dan ingat, caching adalah hal yang baik.

Perlu juga dicatat bahwa browser tidak selalu pelit untuk menyimpan file dalam cache. Browser memiliki kebijakan untuk hal semacam ini, dan mereka biasanya bermain sesuai aturan yang ditetapkan dalam spesifikasi HTTP. Saat browser mengajukan permintaan ke server, bagian dari responsnya adalah header EXPIRES .. tanggal yang memberitahu 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 ke tanggal kedaluwarsa untuk memutuskan apakah itu harus digunakan.

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

Jika Anda tertarik pada info lebih lanjut tentang HTTP atau bagaimana permintaan ini dibuat, buku yang bagus adalah "Situs Web Kinerja Tinggi" oleh Steve Souders. Ini adalah pengantar yang sangat bagus untuk subjek ini.

keparo
sumber
3
Trik cepat menghasilkan string kueri dengan Javascript berfungsi sangat baik selama pengembangan aktif. Saya melakukan hal yang sama dengan PHP.
Alan Turing
2
Ini adalah cara termudah untuk mencapai hasil poster yang diinginkan. Metode mod_rewrite berfungsi dengan baik jika Anda ingin memaksa memuat ulang file .css atau .js SETIAP kali Anda memuat halaman. Metode ini masih memungkinkan caching hingga Anda benar-benar mengubah file dan benar-benar ingin itu memaksa memuat ulang.
scott80109
@ Kemparo, saya memiliki jumlah jquery yang cukup di semua halaman jika saya akan mengubahnya secara manual akan membutuhkan waktu satu bulan. Jika Anda dapat membantu saya menyelesaikan semuanya tanpa menyandi setiap halaman.
cracker
1
Ini sepertinya tidak berfungsi untuk CSS saya ketika saya menggunakan:<link href='myCss.css?dev=14141'...>
Noumenon
3
Ini bukan solusi yang layak. Sejumlah browser yang baik hanya akan menolak untuk me-cache apa pun dengan string kueri di atasnya. Ini adalah alasan mengapa Google, GTMetrix, dan alat-alat serupa akan menaikkan bendera jika Anda memiliki string kueri pada referensi ke konten statis. Walaupun ini jelas merupakan solusi yang layak untuk pengembangan, ini sama sekali bukan solusi untuk produksi. Juga, browser mengontrol caching, bukan server. Server cukup MENYARANKAN kapan harus di-refresh; browser tidak HARUS mendengarkan server (dan seringkali tidak). Perangkat seluler adalah contoh utama dari ini.
Nate I
113

Plugin mod_pagespeed Google untuk apache akan melakukan versi otomatis untuk Anda. Sangat apik.

Ini mem-parsing HTML saat keluar dari server web (bekerja dengan PHP, rails, python, HTML statis - apa saja) dan menulis ulang tautan ke CSS, JS, file gambar sehingga mereka memasukkan kode id. Ini menyajikan file di URL yang dimodifikasi dengan kontrol cache yang sangat panjang. Ketika file berubah, itu secara otomatis mengubah URL sehingga browser harus mengambilnya kembali. Ini pada dasarnya hanya berfungsi, tanpa ada perubahan pada kode Anda. Bahkan akan memperkecil kode Anda di jalan keluar juga.

Leopd
sumber
1
Itu bagus, tapi masih dalam versi beta. Bisakah itu digunakan untuk layanan perusahaan?
Sanghyun Lee
26
Ini SALAH (secara otomatis mengutak-atik sumbernya) saat ini jelas merupakan masalah peramban. Beri kami (pengembang) penyegaran-penyegaran-otak yang nyata: <ctrl> + F5
T4NK3R
25
mod_pagespeed secara fungsional setara dengan langkah build / compile yang sepenuhnya otomatis untuk html / css / js Anda. Saya pikir Anda akan sulit sekali menemukan pengembang yang serius yang berpikir membangun sistem pada dasarnya salah, atau ada yang salah dengan menjadi sepenuhnya otomatis. Analogi build bersih adalah menghapus cache mod_pagespeed: code.google.com/p/modpagespeed/wiki/… ?
Leopd
3
@ T4NK3R mod_pagespeed tidak perlu melakukan apa pun dengan sumber Anda untuk melakukan manajemen cache, hanya disebutkan bahwa itu dapat membantu dengan hal-hal seperti minifikasi. Seperti apakah atau tidak "SALAH", itu benar-benar subjektif. Ini mungkin salah bagi Anda, tetapi itu tidak berarti itu buruk secara instirinsik .
Madbreak
2
Ini bekerja dengan nginx juga meskipun Anda harus membangunnya dari sumber: developers.google.com/speed/pagespeed/module/…
Rohit
93

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

Jadi URL Anda akan seperti itu

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

Anda masih dapat menggunakan aturan penulisan ulang untuk menghapus hash, tetapi keuntungannya adalah sekarang Anda dapat mengatur kebijakan cache Anda menjadi "cache selamanya", karena jika URL-nya sama, itu berarti bahwa file 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 baik-baik saja. Browser HANYA akan memuat ulang file Anda ketika mereka diubah. Jika Anda mengedit dan membatalkannya, tidak ada kesulitan untuk menentukan versi yang harus Anda kembalikan agar pengunjung Anda tidak mengunduh ulang.

levik
sumber
1
sayangnya saya tidak tahu bagaimana cara mengimplementasikannya. Nasihat tolong ... lebih jelasnya ...
Michael Phelps
Implementasi di shell, ruby, dll akan bagus
Peter
3
Solusi yang sangat bagus .. tapi saya pikir itu memakan sumber daya untuk menghitung hash file dalam setiap permintaan file (css, js, gambar, html..etc) untuk setiap kunjungan halaman.
DeepBlue
Ini adalah solusi standar untuk mereka yang menggunakan js atau css bundling dengan gulp, grunt atau webpack, implementasinya berbeda untuk setiap solusi, tetapi hashing file Anda sebagai langkah build adalah umum dan disarankan untuk aplikasi yang dibundel modern
Brandon Søren Culley
@DeepBlue - answer mengatakan "jalankan skrip itu setiap kali CSS berubah" . Itu BUKAN pada setiap kunjungan halaman. OTOH Jawabannya menyisakan detail besar - bagaimana hash yang diubah menjadi bagian dari URL? Saya tidak tahu ...
ToolmakerSteve
71

Tidak yakin mengapa kalian mengambil begitu banyak rasa sakit untuk menerapkan solusi ini.

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

Dalam PHP saya akan melakukannya sebagai:

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

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

Phantom007
sumber
Anda bisa menggunakannya mycss.css?1234567890.
Gavin
3
sangat elegan, meskipun saya telah sedikit memodifikasinya, untuk <link rel="stylesheet" href="mycss.css?<?php echo filemtime('mycss.css') ?>"/>berjaga-jaga jika beberapa argumen di utas ini tentang caching URL dengan variabel GET (dalam format yang disarankan) sudah benar
luke_mclachlan
lebih jauh ke komentar terakhir saya, saya telah melihat bahwa wordpress menggunakan ?ver=siapa yang tahu!
luke_mclachlan
Solusi bagus Selain itu bagi saya, saya menemukan bahwa filemtime tidak berfungsi untuk nama domain yang memenuhi syarat (FQDN), jadi saya menggunakan FQDN untuk bagian href dan $ _SERVER ["DOCUMENT_ROOT"] untuk bagian filemtime. EX: <link rel = "stylesheet" href = "http: //theurl/mycss.css? V = <? Php echo filemtime ($ _ SERVER [" DOCUMENT_ROOT "]. '/Mycss.css')?>" />
rrtx2000
Terima kasih banyak. Sederhana dan bagus. Ini dia dengan Python: progpath = os.path.dirname (sys.argv [0]) def versionize (file): timestamp = os.path.getmtime ('% s /../ web /% s'% (progpath , file)) mengembalikan '% s? v =% s'% (file, cap waktu) cetak <tautan href = "% s" rel = "stylesheet" '' type = "text / css" /> '% versionize ( 'css / main.css')
dlink
52

Anda bisa meletakkan ?foo=1234di akhir impor css / js Anda, mengubah 1234 menjadi apa pun yang Anda suka. Lihat sumber html SO 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 terkait dengan persis bagaimana ini mempengaruhi caching. Saya percaya intinya adalah bahwa GET permintaan, dengan atau tanpa parameter harus dapat di-cachable, sehingga solusi di atas harus bekerja.

Namun, itu tergantung pada server web untuk memutuskan apakah ia ingin mematuhi bagian dari spesifikasi dan browser yang digunakan pengguna, karena itu bisa langsung saja melanjutkan dan meminta versi yang baru.

SCdF
sumber
Omong kosong. String-query (alias. GET parameter) adalah bagian dari URL. Mereka bisa, dan akan di-cache. Ini solusi yang bagus.
troelskn
9
@troelskn: Spesifikasi HTTP 1.1 mengatakan sebaliknya (berkenaan dengan GET dan permintaan HEAD dengan params kueri): cache TIDAK HARUS memperlakukan tanggapan terhadap URI seperti baru kecuali server memberikan waktu kedaluwarsa yang eksplisit. Lihat w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
Michael Johnson
4
Saya mencoba jenis string kueri versi dengan semua browser utama dan mereka DO cache file, spesifikasi atau tidak. Namun, saya pikir lebih baik menggunakan format style.TIMESTAMP.css tanpa menyalahgunakan string kueri karena masih ada kemungkinan bahwa caching perangkat lunak proxy TIDAK akan men-cache file.
Tomas Andrle
34
Patut dicatat, untuk alasan apa pun, Stackoverflow itu sendiri menggunakan metode string kueri.
jason
2
Telah memverifikasi bahwa menggunakan? = Parameter tidak akan membuat browser mengambil kembali file yang di-cache ketika parameter berubah. Satu-satunya cara adalah mengubah nama file itu sendiri secara terprogram di ujung server seperti yang dijawab oleh Kip
arunskrish
41

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

Lihat juga:

John Millikin
sumber
3
Terima kasih, saya kira ini adalah kasus lain di mana ide saya telah dibahas, saya hanya tidak tahu apa namanya sehingga saya tidak pernah menemukannya di pencarian Google.
Kip
27

30 atau lebih jawaban yang ada adalah saran bagus untuk situs web sekitar tahun 2008. Namun, ketika datang ke aplikasi modern, satu halaman (SPA), mungkin sudah saatnya untuk memikirkan kembali beberapa asumsi mendasar ... khususnya gagasan bahwa diinginkan untuk server web untuk hanya melayani versi tunggal, terbaru dari suatu mengajukan.

Bayangkan Anda adalah pengguna yang memiliki M versi SPA yang 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 templat?

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

Aplikasi web mengalami masalah ini ketika dua kondisi terpenuhi:

  • Sumber daya diminta secara tidak sinkron 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, menyelesaikan caching dan "memuat ulang" menjadi sepele:

  1. Menginstal semua file situs ke dirs berversi: /v<release_tag_1>/…files…,/v<release_tag_2>/…files…
  2. Setel header HTTP untuk membiarkan file cache browser selamanya
    • (Atau lebih baik lagi, letakkan semuanya dalam 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 bisa memerlukan memanggil pembuat URL untuk setiap URL di sisi server atau kode sisi klien Anda. Atau Anda bisa memanfaatkan <base>tag dengan cerdas dan mengubah versi saat ini di satu tempat.

† Salah satu cara untuk mengatasi hal ini adalah bersikap agresif memaksa browser untuk memuat ulang semuanya saat versi baru dirilis. Tetapi demi membiarkan semua operasi dalam proses untuk menyelesaikan, mungkin masih paling mudah untuk mendukung setidaknya dua versi secara paralel: v-current dan v-before.

Michael Kropat
sumber
Michael - komentar Anda sangat relevan. Saya datang ke sini tepatnya mencari solusi untuk SPA saya. Saya mendapat beberapa petunjuk, tetapi saya harus menemukan solusi sendiri. Pada akhirnya, saya sangat senang dengan apa yang saya hasilkan sehingga saya menulis posting blog dan jawaban untuk pertanyaan ini (termasuk kode). Terima kasih untuk petunjuknya
statler
Komentar yang bagus Saya tidak bisa mengerti sementara orang terus berbicara tentang penghilang cache dan caching HTTP sebagai solusi nyata untuk masalah caching situs web tanpa menemukan masalah baru dari SPA, seolah-olah ini adalah kasus kecil.
David Casillas
1
Respons yang luar biasa dan strategi yang sangat ideal! Dan poin bonus untuk menyebutkan basetag! Adapun mendukung kode lama: ini tidak selalu merupakan kemungkinan, juga tidak selalu merupakan ide yang baik. Versi kode baru dapat mendukung pemutusan perubahan pada bagian lain aplikasi atau mungkin melibatkan perbaikan darurat, patch kerentanan, dan sebagainya. Saya belum menerapkan strategi ini sendiri, tetapi saya selalu merasa arsitektur secara keseluruhan harus memungkinkan penyebaran untuk menandai versi lama obsoletedan memaksakan pemuatan ulang saat lain kali panggilan tidak sinkron dibuat (atau hanya dengan paksa menonaktifkan semua sesi melalui WebSockets. ).
Jonny Asmar
Senang melihat jawaban yang dipikirkan dengan saksama dalam hal aplikasi satu halaman.
Nate I
Itu "penyebaran biru-hijau" jika Anda ingin mencari info lebih lanjut.
Fil
15

Jangan gunakan foo.css? Versi = 1! Peramban tidak seharusnya menembolok URL dengan variabel GET. Menurut http://www.thinkvitamin.com/features/webapps/serving-javascript-fast , meskipun IE dan Firefox mengabaikannya, Opera dan Safari tidak! Sebaliknya, gunakan foo.v1234.css, dan gunakan aturan penulisan ulang untuk menghapus nomor versi.

airrob
sumber
1
Pertama-tama browser tidak melakukan cache, itu adalah fungsi dari HTTP. Mengapa http peduli dengan struktur URI? Apakah ada referensi officail untuk spesifikasi yang menyatakan cache HTTP harus memahami semantik URI sehingga tidak akan men-cache item dengan string kueri?
AnthonyWJones
13
Peramban web yang menyertakan fungsionalitas objek caching (periksa direktori cache peramban Anda). HTTP adalah protokol termasuk arahan dari server ke klien (proxy, browser, spiders dll) yang menyarankan kontrol cache.
tzot
13

Dalam Laravel (PHP) kita dapat melakukannya dengan mengikuti cara yang jelas dan elegan (menggunakan cap waktu modifikasi file):

<script src="{{ asset('/js/your.js?v='.filemtime('js/your.js')) }}"></script>

Dan serupa untuk CSS

<link rel="stylesheet" href="{{asset('css/your.css?v='.filemtime('css/your.css'))}}">

Contoh html output ( filemtimewaktu pengembalian sebagai cap waktu Unix )

<link rel="stylesheet" href="assets/css/your.css?v=1577772366">
Kamil Kiełczewski
sumber
apa output dari perintah ini dalam html? Dan bagaimana jika saya perlu memperbarui hanya versi seperti? V = 3,? V = 4 dan lain-lain - Tidak memaksa browser untuk memuat pengguna css setiap kali masuk ke situs web
Gediminas
filemtime : "Fungsi ini mengembalikan waktu ketika blok data suatu file sedang ditulis, yaitu waktu ketika konten file diubah." src: php.net/manual/en/function.filemtime.php
Kamil Kiełczewski
11

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

Saya menambahkan kelas negasi titik [^.] Ke regex jadi .number. diabaikan.

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
Nick Johnson
sumber
2
Terima kasih atas masukannya! Sejak saya menulis posting ini saya dibakar oleh ini juga. Solusi saya adalah hanya menulis ulang jika bagian terakhir dari nama file berisi tepat sepuluh digit. (10 digit mencakup semua cap waktu dari 9/9/2001 hingga 11/20/2286.) Saya telah memperbarui jawaban saya untuk memasukkan regex ini:^(.*)\.[\d]{10}\.(css|js)$ $1.$2
Kip
Saya mengerti regex, tapi saya tidak mengerti masalah apa yang Anda selesaikan di [^.]sini. Juga, tidak ada manfaatnya menulis \ddi dalam kelas karakter - \d+akan melakukan hal yang sama. Seperti yang diposting, pola Anda akan cocok dengan sejumlah karakter (dengan rakus), kemudian titik literal, lalu non-titik, lalu satu atau lebih digit, lalu satu titik, lalu cssatau js, kemudian akhir nama file. Tidak cocok dengan input sampel Anda: regex101.com/r/RPGC62/1
mickmackusa
10

Untuk ASP.NET 4.5 dan lebih tinggi Anda dapat menggunakan bundling skrip .

Permintaan http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81untuk bundel AllMyScripts dan berisi pasangan string kueri v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. String kueri v memiliki token nilai yang merupakan pengidentifikasi 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 minifikasi.

pengguna3738893
sumber
Tolong bantu saya, saya tidak melakukan perubahan dalam bundle.config hanya mengubah file css atau js lalu bagaimana saya bisa menyelesaikan masalah caching?
vedankita kumbhar
10

Ini adalah solusi JavaScript murni

(function(){

    // Match this timestamp with the release of your code
    var lastVersioning = Date.UTC(2014, 11, 20, 2, 15, 10);

    var lastCacheDateTime = localStorage.getItem('lastCacheDatetime');

    if(lastCacheDateTime){
        if(lastVersioning > lastCacheDateTime){
            var reload = true;
        }
    }

    localStorage.setItem('lastCacheDatetime', Date.now());

    if(reload){
        location.reload(true);
    }

})();

Di atas akan terlihat kapan terakhir kali pengguna mengunjungi situs Anda. Jika kunjungan terakhir adalah sebelum Anda merilis kode baru, itu digunakanlocation.reload(true) untuk memaksa penyegaran halaman dari server.

Saya biasanya memiliki ini sebagai skrip pertama dalam <head> sehingga dievaluasi sebelum konten lainnya dimuat. Jika perlu memuat ulang, itu hampir tidak terlihat oleh pengguna.

Saya menggunakan penyimpanan lokal untuk menyimpan stempel waktu kunjungan terakhir di browser, tetapi Anda dapat menambahkan cookie ke dalam campuran jika Anda ingin mendukung versi IE yang lebih lama.

Lloyd Banks
sumber
Saya mencoba sesuatu seperti ini, ini hanya akan berfungsi pada halaman yang dimuat ulang, tetapi jika situs memiliki multi halaman yang berbagi css / gambar yang sama maka halaman lain masih akan menggunakan sumber daya lama.
DeepBlue
9

Pos yang menarik. Setelah membaca semua jawaban di sini dikombinasikan dengan fakta bahwa saya tidak pernah memiliki masalah dengan string permintaan "palsu" (yang saya tidak yakin mengapa semua orang begitu enggan untuk menggunakan ini) Saya kira solusinya (yang menghilangkan kebutuhan akan aturan penulisan apache apache seperti dalam jawaban yang diterima) adalah untuk menghitung HASH singkat dari isi file CSS (bukan datetime file) sebagai querystring palsu.

Ini akan menghasilkan sebagai berikut:

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" />

Tentu saja solusi datetime juga menyelesaikan pekerjaan dalam hal mengedit file CSS tapi saya pikir ini adalah tentang konten file css dan bukan tentang file datetime, jadi mengapa dicampur ini?

Michiel
sumber
8

Untuk pengembangan saya, saya menemukan bahwa chrome memiliki solusi hebat.

https://developer.chrome.com/devtools/docs/tips-and-tricks#hard-reload

Dengan alat pengembang terbuka, cukup klik lama tombol refresh dan lepaskan begitu Anda mengarahkan kursor ke "Empty Cache and Hard Reload".

Ini adalah teman terbaik saya, dan merupakan cara yang sangat ringan untuk mendapatkan apa yang Anda inginkan!

Frank Bryce
sumber
Dan jika Anda menggunakan Chrome sebagai lingkungan pengembangan Anda, solusi non-invasif lainnya adalah menonaktifkan cache: Di bawah roda Pengaturan, Anda dapat membatalkan cache cache dengan memilih 'Nonaktifkan cache' (catatan: DevTools harus dapat dilihat / dibuka agar ini bekerja).
Velojet
7

Terima kasih di Kip untuk solusi sempurnanya!

Saya memperluasnya untuk menggunakannya sebagai Zend_view_Helper. Karena klien saya menjalankan halamannya pada virtual host, saya juga memperluasnya untuk itu.

Semoga ini bisa membantu orang lain juga.

/**
 * Extend filepath with timestamp to force browser to
 * automatically refresh them if they are updated
 *
 * This is based on Kip's version, but now
 * also works on virtual hosts
 * @link http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files
 *
 * Usage:
 * - extend your .htaccess file with
 * # Route for My_View_Helper_AutoRefreshRewriter
 * # which extends files with there timestamp so if these
 * # are updated a automatic refresh should occur
 * # RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
 * - then use it in your view script like
 * $this->headLink()->appendStylesheet( $this->autoRefreshRewriter($this->cssPath . 'default.css'));
 *
 */
class My_View_Helper_AutoRefreshRewriter extends Zend_View_Helper_Abstract {

    public function autoRefreshRewriter($filePath) {

        if (strpos($filePath, '/') !== 0) {

            // path has no leading '/'
            return $filePath;
        } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . $filePath)) {

            // file exists under normal path
            // so build path based on this
            $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $filePath);
            return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
        } else {

            // fetch directory of index.php file (file from all others are included)
            // and get only the directory
            $indexFilePath = dirname(current(get_included_files()));

            // check if file exist relativ to index file
            if (file_exists($indexFilePath . $filePath)) {

                // get timestamp based on this relativ path
                $mtime = filemtime($indexFilePath . $filePath);

                // write generated timestamp to path
                // but use old path not the relativ one
                return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
            } else {

                return $filePath;
            }
        }
    }

}

Ceria dan terima kasih.

lony
sumber
7

Belum menemukan pendekatan DOM sisi klien membuat elemen node script (atau css) secara dinamis:

<script>
    var node = document.createElement("script"); 
    node.type = "text/javascript";
    node.src = 'test.js?'+Math.floor(Math.random()*999999999);
    document.getElementsByTagName("head")[0].appendChild(node);
</script>
GreQ
sumber
6

google chrome memiliki Hard Reload serta opsi Empty Cache dan Hard Reload. Anda dapat mengklik dan menahan tombol reload (Dalam Mode Inspeksi) untuk memilih satu.

ajithes111
sumber
Untuk memperjelas, oleh "Periksa Mode", mereka mengacu pada "Dev Tools" alias F12, alias ctrl + shift + i, alias ant menu> More Tools> Developer Tools, alias right click> Inspect Element. Ada juga pengaturan dimakamkan di suatu tempat di alat dev (saya lupa lokasi) untuk memuat ulang keras pada setiap memuat ulang.
Jonny Asmar
5

Anda dapat memaksa "caching sesi-lebar" jika Anda menambahkan id sesi sebagai parameter spureous dari file js / css:

<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>

Jika Anda menginginkan cache versi lebar, Anda bisa menambahkan beberapa kode untuk mencetak tanggal file atau yang serupa. Jika Anda menggunakan Java, Anda dapat menggunakan tag-kustom untuk membuat tautan dengan cara yang elegan.

<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>
helios
sumber
5

Katakanlah Anda memiliki file di:

/styles/screen.css

Anda dapat menambahkan parameter kueri dengan informasi versi ke URI, misalnya:

/styles/screen.css?v=1234

atau Anda dapat menambahkan informasi versi, misalnya:

/v/1234/styles/screen.css

IMHO metode kedua lebih baik untuk file CSS karena mereka dapat merujuk ke gambar menggunakan URL relatif yang berarti bahwa jika Anda menentukan background-imageseperti:

body {
    background-image: url('images/happy.gif');
}

URL-nya secara efektif adalah:

/v/1234/styles/images/happy.gif

Ini berarti bahwa jika Anda memperbarui nomor versi yang digunakan server akan memperlakukan ini sebagai sumber daya baru dan tidak menggunakan versi cache. Jika Anda mendasarkan nomor versi Anda pada Subversion / CVS / etc. revisi ini berarti bahwa perubahan pada gambar yang dirujuk dalam file CSS akan diperhatikan. Itu tidak dijamin dengan skema pertama, yaitu URL images/happy.gifrelatif /styles/screen.css?v=1235adalah /styles/images/happy.gifyang tidak mengandung informasi versi apa pun.

Saya telah menerapkan solusi caching menggunakan teknik ini dengan servlets Java dan hanya menangani permintaan /v/*dengan servlet yang mendelegasikan ke sumber daya yang mendasarinya (yaitu /styles/screen.css). Dalam modus pembangunan Aku menetapkan caching header yang memberitahu klien untuk selalu memeriksa kesegaran sumber daya dengan server (ini biasanya menghasilkan 304 jika Anda mendelegasikan kepada Tomcat DefaultServletdan .css, .js, dll berkas tidak berubah) saat dalam mode penyebaran Saya menetapkan tajuk yang bertuliskan "cache selamanya".

Walter Rumsby
sumber
Cukup menambahkan folder yang dapat Anda ganti namanya bila perlu akan berfungsi jika Anda hanya menggunakan URL relatif. Dan kemudian Anda pastikan untuk mengarahkan ke folder yang tepat dari folder dasar, yaitu dalam PHP: <?php header( 'Location: folder1/login.phtml' ); ?>.
Gruber
1
Dengan menggunakan metode kedua, perubahan pada CSS akan membuat salinan cache dari semua gambar yang dirujuk dengan URL relatif tidak valid, yang mungkin diinginkan atau tidak diinginkan.
TomG
5

Anda bisa menambahkan beberapa nomor acak dengan url CSS / JS seperti

example.css?randomNo=Math.random()
Ponmudi VN
sumber
5

Untuk ASP.NET saya kira solusi berikutnya dengan opsi lanjutan (mode debug / rilis, versi):

File Js atau Css disertakan dengan cara seperti ini:

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />

Global.JsPostfix dan Global.CssPostfix dihitung dengan cara berikut di Global.asax:

protected void Application_Start(object sender, EventArgs e)
{
    ...
    string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
    bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
    int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
    JsPostfix = "";
#if !DEBUG
    JsPostfix += ".min";
#endif      
    JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
    if (updateEveryAppStart)
    {
        Random rand = new Random();
        JsPosfix += "_" + rand.Next();
    }
    ...
}
Ivan Kochurkin
sumber
4

Saya baru-baru ini menyelesaikan ini dengan menggunakan Python. Di sini kodenya (harus mudah diadopsi ke bahasa lain):

def import_tag(pattern, name, **kw):
    if name[0] == "/":
        name = name[1:]
    # Additional HTML attributes
    attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
    try:
        # Get the files modification time
        mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
        include = "%s?%d" % (name, mtime)
        # this is the same as sprintf(pattern, attrs, include) in other
        # languages
        return pattern % (attrs, include)
    except:
        # In case of error return the include without the added query
        # parameter.
        return pattern % (attrs, name)

def script(name, **kw):
    return import_tag("""<script type="text/javascript" """ +\
        """ %s src="/%s"></script>""", name, **kw)

def stylesheet(name, **kw):
    return import_tag('<link rel="stylesheet" type="text/css" ' +\
        """%s href="/%s">', name, **kw) 

Kode ini pada dasarnya menambahkan stempel waktu file sebagai parameter kueri ke URL. Panggilan fungsi berikut

script("/main.css")

akan menghasilkan

<link rel="stylesheet" type="text/css"  href="/main.css?1221842734">

Keuntungannya tentu saja adalah Anda tidak perlu mengubah html lagi, menyentuh file CSS akan secara otomatis memicu pembatalan cache. Bekerja sangat baik dan overhead tidak terlihat.

pi.
sumber
bisakah os.stat () membuat bottleneck?
hoju
@Richard stat bisa menjadi hambatan jika disk sangat lambat dan permintaannya sangat banyak. Dalam hal ini Anda bisa men-cache timestamp di suatu tempat di memori dan membersihkan cache ini pada setiap penyebaran baru. Namun kompleksitas ini tidak akan diperlukan di sebagian besar kasus penggunaan.
pi.
4

Jika Anda menggunakan git + PHP, Anda dapat memuat ulang skrip dari cache setiap kali ada perubahan dalam repo git, menggunakan kode berikut:

exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog);
echo '  <script src="/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL;
readikus
sumber
4

Jika Anda seorang pengembang yang ingin menghindari caching, tab jaringan chrome memiliki opsi cache yang dinonaktifkan. Kalau tidak, Anda bisa melakukannya tanpa kerangka kerja rendering server menggunakan dua tag skrip.

<script type="text/javascript">
    document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
    // can't use myfile.js stuff yet
</script>')
<script type="text/javascript">
    // do something with myfile.js
</script>
astronought
sumber
4

Pertanyaan ini sudah sangat tua, dan muncul hal pertama ketika ada orang yang googles masalah ini. Ini bukan jawaban untuk pertanyaan seperti yang diinginkan op, melainkan jawaban untuk devs dengan masalah ini saat mengembangkan dan menguji. Dan saya tidak dapat memposting pertanyaan baru tentang topik ini karena akan ditandai sebagai duplikat.

Seperti banyak orang lain, saya hanya ingin menghapus caching sebentar.

"keep caching consistent with the file" .. terlalu banyak kerumitan ..

Secara umum, saya tidak keberatan memuat lebih banyak - bahkan memuat lagi file yang tidak berubah - pada sebagian besar proyek - praktis tidak relevan. Saat mengembangkan aplikasi - kami sebagian besar memuat dari disk, di localhost:port - jadi increase in network trafficmasalah ini bukan masalah pemecahan masalah .

Sebagian besar proyek kecil hanya bermain-main - mereka tidak pernah berakhir dalam produksi. jadi bagi mereka Anda tidak perlu lagi ..

Dengan demikian jika Anda menggunakan Alat Dev Chrome , Anda dapat mengikuti pendekatan caching penonaktifan ini seperti pada gambar di bawah ini: cara memaksa chrome untuk memuat ulang file yang di-cache

Dan jika Anda memiliki masalah caching firefox : cara memaksa pemuatan kembali aset di firefox

cara menonaktifkan caching di Firefox saat dalam pengembangan Lakukan ini hanya dalam pengembangan, Anda juga memerlukan mekanisme untuk memaksa memuat ulang untuk produksi, karena pengguna Anda akan menggunakan modul cache lama yang tidak valid jika Anda sering memperbarui aplikasi dan Anda tidak menyediakan mekanisme sinkronisasi cache khusus seperti yang dijelaskan dalam jawaban. atas.

Ya, info ini sudah ada di jawaban sebelumnya tetapi saya masih perlu melakukan pencarian google untuk menemukannya.

Semoga jawaban ini sangat jelas dan sekarang Anda tidak perlu.

AIon
sumber
OP menanyakan sesuatu dan menjawab sesuatu yang lain. Ini bukan tentang memuat paksa di lokal tetapi dalam produksi dan Anda tidak dapat meminta pengguna akhir untuk mengikuti di atas untuk menonaktifkan cache dll.
Jitendra Pancholi
3

Tampaknya semua jawaban di sini menyarankan semacam versi dalam skema penamaan, yang memiliki kelemahan.

Peramban harus mengetahui dengan baik apa yang harus di-cache dan apa yang tidak di-cache dengan membaca respons webservers, khususnya header http - untuk berapa lama sumber daya ini valid? Apakah sumber daya ini diperbarui sejak terakhir kali saya mengambilnya? dan sebagainya.

Jika semuanya dikonfigurasi 'dengan benar', hanya memperbarui file aplikasi Anda harus (pada titik tertentu) menyegarkan cache browser. Misalnya Anda dapat mengkonfigurasi server web Anda untuk memberi tahu browser agar tidak pernah menembolok file (yang merupakan ide buruk).

Penjelasan yang lebih mendalam tentang cara kerjanya ada di sini https://www.mnot.net/cache_docs/#WORK

Commonpike
sumber
3

Cukup tambahkan kode ini, tempat Anda ingin melakukan reload keras (paksa browser untuk memuat ulang file CSS / JS yang di-cache) Lakukan ini di dalam .load agar tidak menyegarkan seperti loop

 $( window ).load(function() {
   location.reload(true);
});
Sandeep Ranjan
sumber
Tidak berfungsi di Chrome. Masih memuat aset dari cache disk
Jason Kim
3

Cukup gunakan kode sisi server untuk menambahkan tanggal file ... dengan cara itu AKAN di-cache dan hanya dimuat ulang ketika file berubah

Di ASP.NET

<link rel="stylesheet" href="~/css/custom.css?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />

<script type="text/javascript" src="~/js/custom.js?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>    

Ini dapat disederhanakan untuk:

<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>

Dengan menambahkan metode ekstensi ke proyek Anda untuk memperluas Halaman:

public static class Extension_Methods
{
    public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
    {
        string sFilePath = oPg.Server.MapPath(sRelPath);
        string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
        string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");

        return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
    }
}
mike
sumber
2

Saya sarankan menerapkan proses berikut:

  • versi file css / js Anda setiap kali Anda menyebarkan, sesuatu seperti: screen.1233.css (nomor tersebut dapat menjadi revisi SVN Anda jika Anda menggunakan sistem versi)

  • minify mereka untuk mengoptimalkan waktu pemuatan

Dan Burzo
sumber