Cegah RequireJS dari Caching Script Wajib

302

RequireJS tampaknya melakukan sesuatu secara internal yang membuat cache file javascript diperlukan. Jika saya membuat perubahan ke salah satu file yang diperlukan, saya harus mengganti nama file agar perubahan diterapkan.

Trik umum menambahkan nomor versi sebagai parameter querystring ke akhir nama file tidak bekerja dengan requireejs <script src="jsfile.js?v2"></script>

Apa yang saya cari adalah cara untuk mencegah cache internal dari skrip yang diperlukan ini tanpa harus mengganti nama file skrip saya setiap kali diperbarui.

Solusi Lintas Platform:

Saya sekarang menggunakan urlArgs: "bust=" + (new Date()).getTime()untuk penghilang cache otomatis selama pengembangan dan urlArgs: "bust=v2"untuk produksi di mana saya menambah num versi hard-kode setelah meluncurkan skrip yang diperlukan diperbarui.

catatan:

@Dustin Getz disebutkan dalam jawaban baru-baru ini bahwa Alat Pengembang Chrome akan melepaskan breakpoints saat debugging ketika file Javascript terus disegarkan seperti ini. Salah satu solusinya adalah menulis debugger;dalam kode untuk memicu breakpoint di sebagian besar debugger Javascript.

Solusi Khusus Server:

Untuk solusi spesifik yang mungkin berfungsi lebih baik untuk lingkungan server Anda seperti Node atau Apache, lihat beberapa jawaban di bawah ini.

BumbleB2na
sumber
Anda yakin ini bukan server atau klien yang melakukan caching? (telah menggunakan js yang diperlukan untuk beberapa bulan sekarang dan belum melihat yang serupa) IE tertangkap melakukan caching hasil tindakan MVC, chrome sedang caching templat html kami tetapi file js semua tampak menyegarkan ketika cache browser telah direset. Saya kira jika Anda ingin menggunakan caching tetapi Anda tidak dapat melakukan hal yang biasa karena permintaan dari js yang diperlukan telah menghapus string kueri yang mungkin menyebabkan masalah?
PJUK
Saya tidak yakin apakah RequireJS menghapus num versi tambahan seperti itu. Mungkin server saya. Menarik bagaimana RequireJS memiliki pengaturan cache-buster, jadi Anda mungkin benar dengan menghapus num versi tambahan saya pada file yang diperlukan.
BumbleB2na
saya memperbarui jawaban saya dengan solusi caching potensial
Dustin Getz
Sekarang saya dapat menambahkan yang berikut ini ke litani yang saya tampilkan di posting blog saya pagi ini: codrspace.com/dexygen/… Dan itu adalah, saya tidak hanya harus menambahkan penghilang cache, tetapi membutuhkan.js mengabaikan penyegaran keras.
Dexygen
Saya bingung tentang kasus penggunaan untuk ini .... Apakah ini untuk modul AMD pemuatan-panas ke front-end atau apa?
Alexander Mills

Jawaban:

457

RequireJS dapat dikonfigurasikan untuk menambahkan nilai ke setiap url skrip untuk penghentian cache.

Dari dokumentasi RequireJS ( http://requirejs.org/docs/api.html#config ):

urlArgs : Argumen string kueri tambahan ditambahkan ke URL yang diperlukan RequireJS untuk mengambil sumber daya. Paling berguna untuk melakukan cache cache ketika browser atau server tidak dikonfigurasi dengan benar.

Contoh, menambahkan "v2" ke semua skrip:

require.config({
    urlArgs: "bust=v2"
});

Untuk tujuan pengembangan, Anda dapat memaksa RequireJS untuk mem-bypass cache dengan menambahkan stempel waktu:

require.config({
    urlArgs: "bust=" + (new Date()).getTime()
});
Phil McCullick
sumber
46
Sangat membantu, terima kasih. Saya menggunakan urlArgs: "bust=" + (new Date()).getTime()penghilang cache secara otomatis selama pengembangan dan urlArgs: "bust=v2"untuk produksi di mana saya menambah num versi hard-code setelah meluncurkan skrip yang diperlukan yang diperbarui.
BumbleB2na
9
... juga sebagai pengoptimal kinerja, Anda dapat menggunakan Math.random () alih-alih (Date baru ()). getTime (). Ini lebih indah, tidak membuat objek dan sedikit lebih cepat jsperf.com/speedcomparison .
Vlad Tsepelev
2
Anda bisa mendapatkannya sedikit lebih kecil:urlArgs: "bust=" + (+new Date)
mrzmyr
11
Saya pikir memecahkan cache setiap kali adalah ide yang buruk. Sayangnya RequireJS tidak menawarkan alternatif lain. Kami menggunakan urlArgs tetapi tidak menggunakan acak atau cap waktu untuk ini. Alih-alih, kami menggunakan Git SHA kami saat ini, dengan cara itu hanya berubah ketika kami menggunakan kode baru.
Ivan Torres
5
Bagaimana varian "v2" ini berfungsi dalam produksi, jika file yang menyediakan string "v2" itu sendiri di-cache? Jika saya merilis aplikasi baru ke dalam produksi, menambahkan "v3" tidak akan melakukan apa-apa, karena aplikasi dengan senang hati terus bekerja dengan file v2 yang di-cache, termasuk konfigurasi lama dengan v2 urlArgs.
Benny Bottema
54

Jangan gunakan urlArgs untuk ini!

Memerlukan banyak skrip untuk menghormati header caching http. (Skrip dimuat dengan dimasukkan secara dinamis <script>, yang berarti permintaan tampak seperti aset lama mana pun yang dimuat).

Sajikan aset javascript Anda dengan tajuk HTTP yang tepat untuk menonaktifkan caching selama pengembangan.

Menggunakan urlArgs yang diperlukan itu berarti setiap breakpoint yang Anda atur tidak akan disimpan di seluruh refresh; Anda akhirnya harus meletakkan debuggerpernyataan di mana-mana dalam kode Anda. Buruk. Saya menggunakan urlArgsaset penghilang cache selama peningkatan produksi dengan git sha; maka saya dapat mengatur aset saya untuk di-cache selamanya dan dijamin tidak pernah memiliki aset basi.

Dalam pengembangan, saya mengolok-olok semua permintaan ajax dengan konfigurasi mockjax yang kompleks , maka saya dapat melayani aplikasi saya dalam mode hanya-javascript dengan server 10 baris python http dengan semua cache dimatikan . Ini telah meningkatkan bagi saya untuk aplikasi "perusahaan" yang cukup besar dengan ratusan titik akhir layanan web yang tenang. Kami bahkan memiliki desainer yang dikontrak yang dapat bekerja dengan basis kode produksi nyata kami tanpa memberinya akses ke kode backend kami.

Dustin Getz
sumber
8
@ JamesP.Wright, karena (setidaknya di Chrome) ketika Anda menetapkan breakpoint untuk sesuatu yang terjadi saat memuat halaman, lalu klik refresh, breakpoint tidak mengenai karena URL telah berubah dan Chrome telah menjatuhkan breakpoint. Saya ingin tahu solusi khusus klien untuk ini.
Drew Noakes
1
Terima kasih, Dustin. Jika Anda menemukan jalan keluarnya, silakan kirim. Sementara itu, Anda dapat menggunakan debugger;kode Anda di mana pun Anda ingin breakpoint bertahan.
BumbleB2na
2
Bagi siapa pun yang menggunakan http-server pada node (npm instal http-server). Anda juga dapat menonaktifkan caching dengan -c-1 (yaitu http-server -c-1).
Yourpalal
5
Anda dapat menonaktifkan caching di Chrome untuk mengatasi masalah debugging selama pengembangan: stackoverflow.com/questions/5690269/…
Deepak Joy
5
+1 hingga !!! JANGAN GUNAKAN urlArgs DALAM PRODUKSI !!! . Bayangkan jika situs web Anda memiliki 1000 file JS (ya, mungkin!) Dan bebannya dikendalikan oleh JS yang diperlukan. Sekarang Anda merilis v2 atau situs Anda di mana hanya beberapa file JS diubah! tetapi dengan menambahkan urlArgs = v2, Anda dipaksa untuk memuat ulang semua 1.000 file JS! Anda akan membayar banyak lalu lintas! hanya file yang dimodifikasi yang harus dimuat ulang, semua yang lain harus direspon dengan status 304 (Tidak Dimodifikasi).
walv
24

Solusi urlArgs memiliki masalah. Sayangnya Anda tidak dapat mengontrol semua server proxy yang mungkin berada di antara Anda dan browser web pengguna Anda. Beberapa dari server proxy ini sayangnya dapat dikonfigurasi untuk mengabaikan parameter URL saat menyimpan file. Jika ini terjadi, versi file JS Anda yang salah akan dikirimkan ke pengguna Anda.

Saya akhirnya menyerah dan menerapkan perbaikan saya sendiri langsung ke require.js. Jika Anda ingin mengubah versi Anda dari perpustakaan Requirejs, solusi ini mungkin cocok untuk Anda.

Anda dapat melihat tambalan di sini:

https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67

Setelah ditambahkan, Anda dapat melakukan sesuatu seperti ini di config:

var require = {
    baseUrl: "/scripts/",
    cacheSuffix: ".buildNumber"
}

Gunakan sistem bangunan atau lingkungan server Anda untuk menggantinya buildNumber dengan id revisi / versi perangkat lunak / warna favorit.

Menggunakan memerlukan seperti ini:

require(["myModule"], function() {
    // no-op;
});

Akan menyebabkan harus meminta file ini:

http://yourserver.com/scripts/myModule.buildNumber.js

Di lingkungan server kami, kami menggunakan aturan penulisan ulang url untuk menghapus buildNumber, dan menyajikan file JS yang benar. Dengan cara ini kita sebenarnya tidak perlu khawatir mengganti nama semua file JS kita.

Patch akan mengabaikan skrip apa pun yang menentukan protokol, dan itu tidak akan memengaruhi file non-JS.

Ini berfungsi baik untuk lingkungan saya, tetapi saya menyadari beberapa pengguna lebih memilih awalan daripada akhiran, seharusnya mudah untuk memodifikasi komit saya sesuai dengan kebutuhan Anda.

Memperbarui:

Dalam diskusi permintaan tarik, penulis yang membutuhkan menyarankan ini mungkin berfungsi sebagai solusi untuk awalan nomor revisi:

var require = {
    baseUrl: "/scripts/buildNumber."
};

Saya belum mencoba ini, tetapi implikasinya adalah ini akan meminta URL berikut:

http://yourserver.com/scripts/buildNumber.myModule.js

Yang mungkin bekerja sangat baik bagi banyak orang yang dapat menggunakan awalan.

Berikut beberapa kemungkinan pertanyaan rangkap:

Membutuhkan JSS dan caching proxy

require.js - Bagaimana saya bisa mengatur versi pada modul yang diperlukan sebagai bagian dari URL?

JBCP
sumber
1
Saya benar-benar ingin melihat pembaruan Anda masuk ke gedung resmi yang diperlukan. Penulis utama requirejs mungkin tertarik juga (lihat jawaban Louis di atas).
BumbleB2na
@ BumbleB2na - jangan ragu untuk mengomentari PullRequest ( github.com/jrburke/requirejs/pull/1017 ), jrburke sepertinya tidak tertarik. Dia menyarankan solusi menggunakan awalan nama file, saya akan memperbarui jawaban saya untuk memasukkan itu.
JBCP
1
Pembaruan yang bagus. Saya pikir saya lakukan seperti saran ini oleh penulis, tapi itu hanya karena saya telah menggunakan konvensi penamaan ini akhir-akhir ini: /scripts/myLib/v1.1/. Saya mencoba menambahkan postfix (atau awalan) ke nama file saya, mungkin karena itulah yang dilakukan jquery, tetapi setelah beberapa saat saya [mulai malas dan] mulai menambah nomor versi di folder induk. Saya pikir itu membuat pemeliharaan lebih mudah bagi saya di situs web besar tetapi, sekarang Anda membuat saya khawatir tentang URL menulis ulang mimpi buruk.
BumbleB2na
1
Sistem front end modern hanya menulis ulang file JS dan dengan jumlah MD5 dalam nama file dan kemudian menulis ulang file HTML untuk menggunakan nama file baru ketika membangun, tetapi itu menjadi rumit dengan sistem warisan di mana kode front end dilayani oleh sisi server.
JBCP
apakah ini berfungsi ketika saya memerlukan beberapa js di dalam file jspx ?, seperti ini<script data-main="${pageContext.request.contextPath}/resources/scripts/main" src="${pageContext.request.contextPath}/resources/scripts/require.js"> <jsp:text/> </script> <script> require([ 'dev/module' ]); </script>
masT
19

Terinspirasi oleh Kedaluwarsa cache pada data-diperlukan. Kami memperbarui skrip penerapan kami dengan tugas semut berikut:

<target name="deployWebsite">
    <untar src="${temp.dir}/website.tar.gz" dest="${website.dir}" compression="gzip" />       
    <!-- fetch latest buildNumber from build agent -->
    <replace file="${website.dir}/js/main.js" token="@Revision@" value="${buildNumber}" />
</target>

Di mana awal main.js terlihat seperti:

require.config({
    baseUrl: '/js',
    urlArgs: 'bust=@Revision@',
    ...
});
bagaimanapun juga
sumber
11

Dalam produksi

urlArgs dapat menyebabkan masalah!

Penulis utama requirejs memilih untuk tidak menggunakanurlArgs :

Untuk aset yang dikerahkan, saya lebih suka menempatkan versi atau hash untuk keseluruhan build sebagai direktori build, kemudian hanya memodifikasi baseUrlkonfigurasi yang digunakan untuk proyek untuk menggunakan direktori yang diversi sebagai baseUrl. Maka tidak ada file lain yang berubah, dan itu membantu menghindari beberapa masalah proxy di mana mereka mungkin tidak men-cache URL dengan string kueri di atasnya.

[Styling milikku.]

Saya mengikuti saran ini.

Dalam pengembangan

Saya lebih suka menggunakan server yang secara cerdas menyimpan file yang mungkin sering berubah: server yang memancarkan Last-Modifieddan merespons If-Modified-Sincedengan 304 jika perlu. Bahkan server yang didasarkan pada Node's express set untuk melayani file-file statis melakukan hal ini. Itu tidak perlu melakukan apa pun ke browser saya, dan tidak mengacaukan breakpoints.

Louis
sumber
Poin bagus tetapi, jawaban Anda khusus untuk lingkungan server Anda. Mungkin alternatif yang baik untuk orang lain yang tersandung pada ini adalah rekomendasi baru-baru ini untuk menambahkan nomor versi ke nama file, bukan parameter querystring. Berikut info lebih lanjut tentang subjek itu: stevesouders.com/blog/2008/08/23/…
BumbleB2na
Kami menjalankan masalah khusus ini dalam sistem produksi. Saya merekomendasikan reving nama file Anda daripada menggunakan parameter.
JBCP
7

Saya mengambil cuplikan ini dari AskApache dan memasukkannya ke dalam file .conf terpisah dari server web Apache lokal saya (dalam kasus saya /etc/apache2/others/preventcaching.conf):

<FilesMatch "\.(html|htm|js|css)$">
FileETag None
<ifModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</ifModule>
</FilesMatch>

Untuk pengembangan ini berfungsi dengan baik tanpa perlu mengubah kode. Sedangkan untuk produksi, saya mungkin menggunakan pendekatan @ dvtoever.

myrho
sumber
6

Perbaikan Cepat untuk Pengembangan

Untuk pengembangan, Anda bisa menonaktifkan cache di Alat Dev Chrome ( Menonaktifkan cache Chrome untuk pengembangan situs web ). Penonaktifan cache hanya terjadi jika dialog dev tools terbuka, jadi Anda tidak perlu khawatir untuk mengaktifkan opsi ini setiap kali Anda melakukan penelusuran biasa.

Catatan: Menggunakan ' urlArgs ' adalah solusi yang tepat dalam produksi sehingga pengguna mendapatkan kode terbaru. Tetapi itu membuat debugging sulit karena chrome membatalkan breakpoints dengan setiap refresh (karena file 'baru' dilayani setiap kali).

Deepak Joy
sumber
3

Saya tidak merekomendasikan menggunakan ' urlArgs ' untuk cache yang meledak dengan RequireJS. Karena ini tidak menyelesaikan masalah sepenuhnya. Memperbarui versi no akan menghasilkan pengunduhan semua sumber daya, meskipun Anda baru saja mengubah satu sumber daya.

Untuk menangani masalah ini saya sarankan menggunakan modul Grunt seperti 'filerev' untuk membuat revisi no. Yang paling penting, saya telah menulis tugas khusus di Gruntfile untuk memperbarui revisi ini, kapan pun diperlukan.

Jika perlu saya dapat membagikan potongan kode untuk tugas ini.

Amit Sagar
sumber
Saya menggunakan kombinasi grunt-filerev dan grunt-cache-buster untuk menulis ulang file Javascript.
Ian Jamieson
2

Ini adalah bagaimana saya melakukannya di Django / Flask (dapat dengan mudah disesuaikan dengan sistem bahasa / VCS lainnya):

Di Anda config.py(saya menggunakan ini di python3, jadi Anda mungkin perlu men-tweak encoding di python2)

import subprocess
GIT_HASH = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip().decode('utf-8')

Kemudian di templat Anda:

{% if config.DEBUG %}
     require.config({urlArgs: "bust=" + (new Date().getTime())});
{% else %}
    require.config({urlArgs: "bust=" + {{ config.GIT_HASH|tojson }}});
{% endif %}
  • Tidak memerlukan proses pembuatan manual
  • Hanya berjalan git rev-parse HEADsekali ketika aplikasi dimulai, dan menyimpannya di configobjek
Stephen Fuhry
sumber
0

Solusi dinamis (tanpa urlArgs)

Ada solusi sederhana untuk masalah ini, sehingga Anda dapat memuat nomor revisi unik untuk setiap modul.

Anda dapat menyimpan fungsi requireejs.load yang asli, menimpa dengan fungsi Anda sendiri dan mem-parsing url Anda yang telah dimodifikasi ke yang asli needsejs.load lagi:

var load = requirejs.load;
requirejs.load = function (context, moduleId, url) {
    url += "?v=" + oRevision[moduleId];
    load(context, moduleId, url);
};

Dalam proses pembangunan kami, saya menggunakan "gulp-rev" untuk membangun file manifes dengan semua revisi dari semua modul yang digunakan. Versi sederhana dari tugas teguk saya:

gulp.task('gulp-revision', function() {
    var sManifestFileName = 'revision.js';

    return gulp.src(aGulpPaths)
        .pipe(rev())
        .pipe(rev.manifest(sManifestFileName, {
        transformer: {
            stringify: function(a) {
                var oAssetHashes = {};

                for(var k in a) {
                    var key = (k.substr(0, k.length - 3));

                    var sHash = a[k].substr(a[k].indexOf(".") - 10, 10);
                    oAssetHashes[key] = sHash;
                }

                return "define([], function() { return " + JSON.stringify(oAssetHashes) + "; });"
            }
        }
    }))
    .pipe(gulp.dest('./'));
});

ini akan menghasilkan AMD-module dengan angka revisi ke moduleNames, yang dimasukkan sebagai 'oRevision' di main.js, di mana Anda menimpa fungsi needsejs.load seperti yang ditunjukkan sebelumnya.

tikar
sumber
-1

Ini sebagai tambahan untuk jawaban yang diterima @phil mccull.

Saya menggunakan metodenya tetapi saya juga mengotomatiskan proses dengan membuat template T4 untuk menjalankan pra-membangun.

Perintah Pra-Bangun:

set textTemplatingPath="%CommonProgramFiles(x86)%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
if %textTemplatingPath%=="\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe" set textTemplatingPath="%CommonProgramFiles%\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\texttransform.exe"
%textTemplatingPath% "$(ProjectDir)CacheBuster.tt"

masukkan deskripsi gambar di sini

Templat T4:

masukkan deskripsi gambar di sini

File yang Dihasilkan: masukkan deskripsi gambar di sini

Simpan dalam variabel sebelum require.config.js dimuat: masukkan deskripsi gambar di sini

Referensi di require.config.js:

masukkan deskripsi gambar di sini

Zach Painter
sumber
2
Kemungkinan karena solusi Anda untuk masalah JavaScript adalah banyak kode C #. Ini juga merupakan kumpulan pekerjaan ekstra dan kode untuk melakukan sesuatu yang pada akhirnya dilakukan dengan cara yang persis sama dengan jawaban yang diterima.
mAAdhaTTah
@mAAdhaTTah Anda bisa melakukan ini dengan bahasa sisi server apa pun ... tidak harus c #. Ini juga mengotomatiskan proses, memperbarui tembolok cache ketika Anda membangun versi baru proyek memastikan bahwa pelanggan selalu caching versi terbaru dari skrip. Saya tidak berpikir itu layak penurunan harga negatif. Itu hanya memperluas jawaban dengan menawarkan pendekatan otomatis untuk solusi.
Zach Painter
Saya pada dasarnya menciptakan ini karena ketika memelihara aplikasi yang menggunakan require.js, saya merasa cukup menjengkelkan karena harus secara manual mengomentari "(Tanggal baru ()). GetTime ()) dan menghapus tanda komentar pada cachebuster statis setiap kali saya memperbarui aplikasi Mudah dilupakan. Tiba-tiba pelanggan memverifikasi perubahan dan melihat skrip cache sehingga mereka berpikir tidak ada yang berubah .. Semua karena Anda lupa mengubah cachebuster. Sedikit kode tambahan ini menghapus kemungkinan terjadinya hal itu.
Zach Painter
2
Saya tidak menandainya, saya tidak tahu apa yang sebenarnya terjadi. Saya hanya menyarankan kode yang bukan hanya bukan js, tetapi bahasa templat C #, tidak akan membantu para pengembang JS yang mencoba mendapatkan jawaban untuk masalah mereka.
mAAdhaTTah
-2

Dalam kasus saya, saya ingin memuat formulir yang sama setiap kali saya mengklik, saya tidak ingin perubahan yang saya buat pada file tetap. Ini mungkin tidak relevan dengan postingan ini secara tepat, tetapi ini bisa menjadi solusi potensial di sisi klien tanpa menetapkan konfigurasi yang diperlukan. Alih-alih mengirim konten secara langsung, Anda dapat membuat salinan file yang diperlukan dan mempertahankan file yang sebenarnya tetap utuh.

LoadFile(filePath){
    const file = require(filePath);
    const result = angular.copy(file);
    return result;
}
Mahib
sumber