RequireJS'nin Gerekli Komut Dosyalarını Önbelleğe Almasını Engelleme


302

RequireJS, javascript dosyalarını önbelleğe alan dahili bir şey yapıyor gibi görünüyor. Gerekli dosyalardan birinde değişiklik yaparsam değişikliklerin uygulanabilmesi için dosyayı yeniden adlandırmam gerekir.

Dosya adının sonuna sorgu dizesi parametresi olarak bir sürüm numarası eklemenin yaygın hilesi requirjs ile çalışmaz <script src="jsfile.js?v2"></script>

Ne aradığımı, her güncellendiğinde komut dosyalarımı yeniden adlandırmak zorunda kalmadan RequireJS gerekli komut dosyalarının bu iç önbelleklemesini önlemek için bir yoldur.

Platformlar Arası Çözüm:

Şimdi urlArgs: "bust=" + (new Date()).getTime()geliştirme sırasında otomatik önbellek bozma ve urlArgs: "bust=v2"güncellenmiş gerekli bir komut dosyası yayınlandıktan sonra sabit kodlu sürüm numarasını artırdığım üretim için kullanıyorum.

Not:

@Dustin Getz, son bir cevapta, Javascript dosyaları bu şekilde sürekli yenilendiğinde hata ayıklama sırasında kesme noktaları bırakacağından bahsetti. Bir geçici çözüm, debugger;çoğu Javascript hata ayıklayıcısında bir kesme noktasını tetiklemek için kod yazmaktır .

Sunucuya Özgü Çözümler:

Düğüm veya Apache gibi sunucu ortamınız için daha iyi çalışabilecek belirli çözümler için aşağıdaki yanıtlardan bazılarına bakın.


Bunun önbelleğe alma işlemini yapan sunucunun veya istemcinin olmadığından emin misiniz? (birkaç aydır gerekli js kullanıyordum ve benzer bir şey fark etmedim) IE, MVC eylem sonuçlarını önbelleğe alırken yakalandı, krom html şablonlarımızı önbelleğe aldı, ancak tarayıcı önbelleği sıfırlandığında js dosyalarının tümü yenileniyor gibi görünüyor. Önbelleğe almak için arıyorlardı ama gerekli js istekleri soruna neden olabilir sorgu dizesi kaldırıyordu çünkü her zamanki yapamam varsayalım?
PJUK

RequireJS'nin ekteki sürüm numaralarını bu şekilde kaldırıp kaldırmayacağından emin değilim. Sunucum olabilir. RequireJS'nin bir önbellek bozucu ayarına sahip olması ilginç, bu yüzden gerekli dosyalara eklenen sürüm numaralarını kaldırmak konusunda haklı olabilirsiniz.
BumbleB2na

cevabımı potansiyel bir önbellekleme çözümü ile güncelledim
Dustin Getz

Şimdi bu sabah blog yazımda ortaya koyduğum litana aşağıdakileri ekleyebilirim: codrspace.com/dexygen/… Ve bu sadece önbellek bozma eklemek zorunda değilim, requir.js sert bir yenilemeyi yok sayar.
Dexygen

Bunun kullanım durumu hakkında kafam karıştı ... Bu AMD modüllerini ön uçta yeniden yüklemek için mi yoksa ne?
Alexander Mills

Yanıtlar:


457

RequireJS, önbellek bozma için kod URL'lerinin her birine bir değer eklemek üzere yapılandırılabilir.

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

urlArgs : RequireJS'nin kaynakları almak için kullandığı URL'lere eklenen fazladan sorgu dizesi bağımsız değişkenleri. Tarayıcı veya sunucu doğru yapılandırılmadığında büstü önbelleklemek için en kullanışlıdır.

Tüm komut dosyalarına "v2" ekleme örneği:

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

Geliştirme amacıyla, bir zaman damgası ekleyerek RequireJS'yi önbelleği atlamaya zorlayabilirsiniz:

require.config({
    urlArgs: "bust=" + (new Date()).getTime()
});

46
Çok faydalı, teşekkürler. Ben kullanıyorum urlArgs: "bust=" + (new Date()).getTime()için otomatik önbellek bozma gelişimi sırasında ve urlArgs: "bust=v2"ben güncelleştirilmiş gerekli komut dosyası kullanmaya başladıktan sonra kodlanmış versiyonu num artırmaz üretim için.
BumbleB2na

9
... performans iyileştirici olarak Math.random () 'u (new Date ()) yerine kullanabilirsiniz. getTime (). Daha fazla güzellik, nesne yaratma ve biraz daha hızlı jsperf.com/speedcomparison .
Vlad Tsepelev

2
Biraz daha küçük alabilirsiniz:urlArgs: "bust=" + (+new Date)
mrzmyr

11
Bence her seferinde önbelleği yıkmak korkunç bir fikir. Ne yazık ki RequireJS başka bir alternatif sunmuyor. UrlArgs kullanıyoruz ancak bunun için rastgele veya zaman damgası kullanmıyoruz. Bunun yerine, mevcut Git SHA'mızı kullanırız, bu şekilde yalnızca yeni kod dağıtırken değişir.
Ivan Torres

5
"V2" dizesini sağlayan dosya önbelleğe alınırsa, bu "v2" varyantı üretimde nasıl çalışabilir? Üretime yeni bir uygulama yayınlarsam, uygulama v2 ile eski yapılandırma da dahil olmak üzere önbelleğe alınmış v2 dosyalarıyla mutlu bir şekilde çalışmaya devam ettiğinden "v3" eklemek hiçbir şey yapmaz urlArgs.
Benny Bottema

54

Bunun için urlArgs kullanmayın!

Komut dosyası yüklerini http önbellek başlıklarına saygı göstermeyi gerektirir. (Komut dosyaları dinamik olarak eklenmiş olarak yüklenir <script>, yani istek eski varlıkların yüklenmesi gibi görünür.)

Geliştirme sırasında önbelleğe almayı devre dışı bırakmak için javascript varlıklarınızı uygun HTTP üstbilgileriyle sunun.

Require's urlArgs kullanmak, ayarladığınız kesme noktalarının yenilemeler arasında korunmayacağı anlamına gelir; debuggerkodunuzun her yerine ifadeler koymanız gerekir. Kötü. urlArgsGit sha ile üretim yükseltmeleri sırasında önbellek bozan varlıklar için kullanıyorum ; varlıklarımı sonsuza kadar önbelleğe alınacak şekilde ayarlayabilir ve hiçbir zaman eski varlıkları olmayacağı garanti edilebilir.

Geliştirmede, tüm ajax isteklerini karmaşık bir mockjax yapılandırmasıyla alay ediyorum , daha sonra tüm önbellekleme kapalı bir 10 satır python http sunucusu ile uygulamamı sadece javascript modunda sunabilirim . Bu benim için yüzlerce dinlendirici web servis uç noktaları ile oldukça büyük bir "girişim" uygulaması için ölçeklendirdi. Hatta, arka uç kodumuza erişmesine izin vermeden gerçek üretim kod tabanımızla çalışabilen sözleşmeli bir tasarımcımız var.


8
@ JamesP.Wright, çünkü (en azından Chrome'da) sayfa yüklemesinde gerçekleşen bir şey için bir kesme noktası ayarladığınızda, yenile'yi tıklayın, URL değiştiği ve Chrome kesme noktasını düşürdüğü için kesme noktasına vurulmaz. Bunun için yalnızca müşteriye yönelik bir geçici çözüm bilmek isterim.
Drew Noakes

1
Teşekkürler Dustin. Bu konuda bir yol bulursanız lütfen gönderin. Bu arada, debugger;bir kesme noktasının kalmasını istediğiniz her yerde kodunuzda kullanabilirsiniz .
BumbleB2na

2
Düğümde http-sunucusu kullanan herkes için (npm http-server'ı kurun). Ayrıca -c-1 (yani http-server -c-1) ile önbelleğe almayı devre dışı bırakabilirsiniz.
Yourpalal

5
Geliştirme sırasında hata ayıklama sorununu çözmek için Chrome'da önbelleğe almayı devre dışı bırakabilirsiniz: stackoverflow.com/questions/5690269/…
Deepak Joy

5
+1 ila !!! ÜRETİMDE urlArgs KULLANMAYIN !!! . Web sitenizin 1000 JS dosyasına (evet, mümkün!) Sahip olduğunu ve yüklerinin requiredJS tarafından kontrol edildiğini düşünün. Şimdi v2'yi veya sadece birkaç JS dosyasının değiştirildiği sitenizi serbest bırakıyorsunuz! ancak urlArgs = v2 ekleyerek 1000 JS dosyasının tümünü yeniden yüklemeye zorlarsınız! çok fazla trafik ödeyeceksiniz! yalnızca değiştirilen dosyalar yeniden yüklenmeli, diğerlerine 304 durumu (Değiştirilmedi) yanıtlanmalıdır.
walv

24

UrlArgs çözümünde sorunlar var. Maalesef, sizinle web tarayıcınız arasında olabilecek tüm proxy sunucularını denetleyemezsiniz. Bu proxy sunucularının bazıları maalesef dosyaları önbelleğe alırken URL parametrelerini yok sayacak şekilde yapılandırılabilir. Bu durumda, JS dosyanızın yanlış sürümü kullanıcılarınıza teslim edilir.

Sonunda vazgeçtim ve doğrudan fix.js'ye kendi düzeltmelerimi uyguladım. Requijs kütüphanesinin sürümünüzü değiştirmek isterseniz, bu çözüm sizin için işe yarayabilir.

Yamayı burada görebilirsiniz:

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

Eklendikten sonra, gerekli yapılandırmanızda böyle bir şey yapabilirsiniz:

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

buildNumberBir revizyon kimliği / yazılım sürümü / favori renkle değiştirmek için derleme sisteminizi veya sunucu ortamınızı kullanın .

Kullanmak şöyle gerektirir:

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

Bu dosyayı talep etmenizi gerektirecek:

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

Sunucu ortamımızda, buildNumber'ı ayıklamak ve doğru JS dosyasını sunmak için url yeniden yazma kurallarını kullanırız. Bu şekilde tüm JS dosyalarımızı yeniden adlandırmak konusunda endişelenmemize gerek kalmaz.

Düzeltme eki, bir protokol belirten herhangi bir komut dosyasını yoksayar ve JS olmayan dosyaları etkilemez.

Bu, ortamım için iyi çalışıyor, ancak bazı kullanıcıların bir sonek yerine bir önek tercih edeceğini anlıyorum, taahhüdümü ihtiyaçlarınıza uyacak şekilde değiştirmek kolay olmalı.

Güncelleme:

Çekme isteği tartışmasında, talepler yazarı, bunun düzeltme numarasını öneklemek için bir çözüm olarak çalışabileceğini önermektedir:

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

Ben bunu denemedim, ama ima bu aşağıdaki URL isteyecektir:

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

Bir önek kullanabilen birçok insan için çok iyi sonuç verebilir.

Bazı olası yinelenen sorular:

JS ve proxy önbellekleme gerektirir

URL'nin bir parçası olarak gerekli modüllerde nasıl sürüm ayarlayabilirim?


1
Gerçekten güncellemenizin resmi requirjs derlemesine girdiğini görmek istiyorum. Requijs'in asıl yazarı da ilgilenebilir (bkz. Yukarıdaki Louis'in cevabı).
BumbleB2na

@ BumbleB2na - PullRequest ( github.com/jrburke/requirejs/pull/1017 ) hakkında yorum yapmaktan çekinmeyin , jrburke ilgilenmiyor gibi görünüyordu. Dosya adı öneki kullanarak bir çözüm öneriyor, cevabımı buna dahil etmek için güncelleyeceğim.
JBCP

1
Güzel güncelleme. Ben yazar tarafından bu öneriyi beğendi sanıyorsun ama son zamanlarda bu adlandırma kuralını kullanarak oldum çünkü bu sadece: /scripts/myLib/v1.1/. Muhtemelen jquery yaptığı için dosya adıma postfix (veya önek) eklemeyi denedim, ama bir süre sonra ben [tembel var ve] üst klasörde bir sürüm numarasını artırmaya başladı. Bence büyük bir web sitesinde bakım benim için daha kolay oldu, ama şimdi URL yeniden yazma kabusları hakkında endişeleniyorsun.
BumbleB2na

1
Modern ön uç sistemleri sadece JS dosyalarını ve dosya adında bir MD5 toplamı ile yeniden yazar ve ardından HTML dosyalarını yeniden oluştururken yeni dosya adlarını oluştururken yeniden yazar, ancak bu, ön uç kodunun sunucu tarafında sunulduğu eski sistemlerle zorlaşır.
JBCP

jspx dosyası içinde bazı js gerektiğinde bu çalışır ?, böyle<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

Require data-main üzerinde Expire önbelleğinden esinlenerek, dağıtım komut dosyamızı aşağıdaki karınca göreviyle güncelledik:

<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>

Main.js'nin başlangıcı aşağıdaki gibi:

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

11

Üretimde

urlArgs sorunlara neden olabilir!

Requijs'in baş yazarı şunları kullanmamayı tercih ederurlArgs :

Konuşlandırılmış varlıklar için, tüm derleme için sürüm veya karmayı bir derleme dizini olarak koymayı tercih ediyorum, sonra sadece baseUrlbu sürüm dizini baseUrl. Daha sonra başka hiçbir dosya değişmez ve üzerinde bir sorgu dizesi bulunan bir URL'yi önbelleğe alamayacakları bazı proxy sorunlarının önlenmesine yardımcı olur .

[Madeni şekillendir.]

Bu tavsiyeye uyuyorum.

Geliştirilmekte

Sık sık değişebilen dosyaları akıllıca önbelleğe alan bir sunucu kullanmayı tercih ederim: uygun olduğunda 304 ile yayan Last-Modifiedve yanıt veren bir sunucu If-Modified-Since. Düğümün statik dosyaları sunmak için ifade kümesine dayanan bir sunucu bile bunu kutudan çıkarır. Tarayıcım için hiçbir şey yapmayı gerektirmez ve kesme noktalarını bozmaz.


İyi puanlar, ancak cevabınız sunucu ortamınıza özgüdür. Belki de bu sorunla karşılaşan herkes için iyi bir alternatif, dosya adına bir sorgu dizesi parametresi yerine sürüm numarası eklemek için son öneridir. Bu konu hakkında daha fazla bilgi: stevesouders.com/blog/2008/08/23/…
BumbleB2na

Bir üretim sisteminde bu sorunla karşılaşıyoruz. Parametre kullanmak yerine dosya adlarınızı değiştirmenizi öneririz.
JBCP

7

Bu parçacığı AskApache'den aldım ve yerel Apache web sunucumun ayrı bir .conf dosyasına koydum (benim durumumda /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>

Geliştirme için bu kodu değiştirmek gerek kalmadan iyi çalışır. Prodüksiyona gelince, @ dvtoever yaklaşımını kullanabilirim.


6

Geliştirme için Hızlı Düzeltme

Geliştirme için yalnızca Chrome Geliştirme Araçları'nda ( Web sitesi geliştirme için Chrome önbelleğini devre dışı bırakma) önbelleği devre dışı bırakabilirsiniz . Önbellek devre dışı bırakma yalnızca geliştirici araçları iletişim kutusu açıksa gerçekleşir, bu nedenle düzenli tarama yaptığınızda bu seçeneği değiştirmek konusunda endişelenmenize gerek yoktur.

Not: Kullanıcıların en son kodu almaları için üretimde ' urlArgs ' kullanmak doğru çözümdür. Ancak hata ayıklamayı zorlaştırır, çünkü krom her yenilemede kesme noktalarını geçersiz kılar (çünkü her seferinde 'yeni' bir dosya sunuluyor).


3

RequireJS ile önbellek patlaması için ' urlArgs ' kullanılmasını önermiyorum. Bu sorunu tam olarak çözmediği için. Sürüm no'yu güncellemek, tek bir kaynağı değiştirmiş olmanıza rağmen tüm kaynakların indirilmesine neden olur.

Bu sorunu çözmek için revizyon no. Oluşturmak için 'filerev' gibi Grunt modüllerini kullanmanızı tavsiye ederim. Bunun üzerine, gerekli olan her yerde revizyonu güncellemek için Gruntfile'da özel bir görev yazdım.

Gerekirse bu görev için kod snippet'ini paylaşabilirim.


Javascript dosyalarını yeniden yazmak için grunt-filerev ve grunt-cache-buster kombinasyonunu kullanıyorum.
Ian Jamieson

2

Django / Flask (ben diğer diller / VCS sistemlerine kolayca adapte edilebilir) bunu nasıl yaparım:

Reklamlara config.py(eğer python2 içinde kodlamayı değişiklikler yapmanızı gerektirecek yüzden python3 bu kullanın)

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

Ardından şablonunuzda:

{% if config.DEBUG %}
     require.config({urlArgs: "bust=" + (new Date().getTime())});
{% else %}
    require.config({urlArgs: "bust=" + {{ config.GIT_HASH|tojson }}});
{% endif %}
  • Manuel oluşturma işlemi gerektirmez
  • git rev-parse HEADUygulama başlatıldığında yalnızca bir kez çalışır ve confignesnede saklar

0

Dinamik çözüm (urlArgs olmadan)

Her modül için benzersiz bir revizyon numarası yükleyebilmeniz için bu sorunun basit bir çözümü vardır.

Orijinal requirjs.load işlevini kaydedebilir, kendi işlevinizle üzerine yazabilir ve değiştirilmiş URL'nizi orijinal requirjs.load ile tekrar ayrıştırabilirsiniz:

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

Yapım sürecimizde "gulp-rev" i kullandığımız tüm modüllerin tüm revizyonlarıyla bir manifesto dosyası oluşturmak için kullandım. Benim yutkunma görevimin basitleştirilmiş versiyonu:

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('./'));
});

bu, main.js içinde 'oRevision' olarak eklenen ve daha önce gösterildiği gibi requirjs.load işlevinin üzerine yazdığınız moduleNames için revizyon numaralarına sahip bir AMD modülü oluşturur.


-1

Bu @phil mccull'un kabul ettiği cevaba ek olarak.

Ben onun yöntemini kullanmak ama aynı zamanda önceden inşa çalıştırmak için bir T4 şablonu oluşturarak süreci otomatikleştirmek.

Yapım Öncesi Komutlar:

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"

resim açıklamasını buraya girin

T4 şablonu:

resim açıklamasını buraya girin

Oluşturulan Dosya: resim açıklamasını buraya girin

Requir.config.js yüklenmeden önce değişkeni saklayın: resim açıklamasını buraya girin

Requir.config.js dosyasında referans:

resim açıklamasını buraya girin


2
Bir JavaScript sorununa çözümünüz bir grup C # kodu olduğu için. Bu da sonuçta kabul edilen cevapla aynı şekilde yapılan bir şey yapmak için bir sürü ekstra çalışma ve koddur.
mAAdhaTTah

@mAAdhaTTah Bunu herhangi bir sunucu tarafı dili ile yapabilirsiniz ... c # olması gerekmez. Ayrıca bu, projenin yeni bir sürümünü oluşturduğunuzda müşterinin komut dosyalarının her zaman en son sürümünü önbelleğe almasını sağlayarak önbellek büstünü güncelleyerek işlemi otomatik hale getirir. Bunun olumsuz bir düşüşü hak ettiğini düşünmüyorum. Çözüme otomatik bir yaklaşım sunarak yanıtı genişletiyor.
Zach Painter

Temelde bunu yarattım çünkü requir.js kullanılan bir uygulamayı sürdürürken, "(yeni Date ()). GetTime ()) 'i manuel olarak yorumlamak ve uygulamayı her güncellediğimde statik önbellek engellemesini kaldırmak oldukça can sıkıcı buldum . Kolay unutmak onlar hiçbir şey değişmedi düşünüyorum böylece tüm aniden müşteri teyid edildikten ve önbelleğe senaryoyu görüyor .. sadece önbellek engelleme değiştirmek için unuttum çünkü tüm ekstra kod Bu biraz Bunun olma şansı siler...
Zach Painter

2
İşaretlemedim, gerçekte ne olduğunu bilmiyorum. Ben sadece js değil, bir C # templating dil, kod önermek JS sorunlarına bir yanıt almaya çalışırken JS devs için yararlı olmayacak.
mAAdhaTTah

-2

Benim durumumda, her tıkladığımda aynı formu yüklemek istedim, dosyada yaptığım değişikliklerin kalmasını istemedim. Bu gönderiyle tam olarak ilgili olmayabilir, ancak bu istek için yapılandırılmadan istemci tarafında potansiyel bir çözüm olabilir. İçeriği doğrudan göndermek yerine, gerekli dosyanın bir kopyasını oluşturabilir ve asıl dosyayı olduğu gibi bırakabilirsiniz.

LoadFile(filePath){
    const file = require(filePath);
    const result = angular.copy(file);
    return result;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.