RequireJSが必要なスクリプトをキャッシュしないようにする


302

RequireJSは、必要なJavaScriptファイルをキャッシュするために内部的に何かをしているようです。必要なファイルの1つを変更した場合、変更を適用するためにファイルの名前を変更する必要があります。

バージョン番号をquerystringパラメータとしてファイル名の最後に追加する一般的なトリックは、requirejsでは機能しません <script src="jsfile.js?v2"></script>

私が探しているのは、スクリプトファイルが更新されるたびにスクリプトファイルの名前を変更する必要なく、このRequireJS必須スクリプトの内部キャッシュを防ぐ方法です。

クロスプラットフォームソリューション:

現在urlArgs: "bust=" + (new Date()).getTime()、開発中の自動キャッシュ無効urlArgs: "bust=v2"化と、更新された必要なスクリプトをロールアウトした後にハードコードされたバージョン番号をインクリメントする本番環境で使用しています。

注意:

@Dustin Getzは最近の回答で、このようにJavaScriptファイルが継続的に更新されると、Chromeデベロッパーツールがデバッグ中にブレークポイントを削除することを述べました。1つの回避策はdebugger;、ほとんどのJavaScriptデバッガーでブレークポイントをトリガーするコードを記述することです。

サーバー固有のソリューション:

NodeやApacheなどのサーバー環境に適している特定のソリューションについては、以下の回答のいくつかを参照してください。


これはキャッシュを実行しているサーバーまたはクライアントではないと確信していますか?(必要なjsを数か月間使用していて、同様のことに気づいていません)IEがMVCアクションの結果をキャッシュしていることを検出しました、Chromeはhtmlテンプレートをキャッシュしていましたが、ブラウザーのキャッシュがリセットされるとjsファイルはすべて更新されるようです。キャッシュを利用しようとしているのに、必要なjsからのリクエストが問題を引き起こす可能性のあるクエリ文字列を削除していたため、通常のことはできないと思いますか?
PJUK 2011

RequireJSがそのような追加されたバージョン番号を削除するかどうかはわかりません。それは私のサーバーだったのかもしれません。RequireJSのキャッシュ無効化の設定は興味深いので、必要なファイルに追加されたバージョン番号を削除するのは正しいかもしれません。
BumbleB2na 2011

潜在的なキャッシングソリューションで回答を更新しました
ダスティンゲッツ

今朝私は私のブログ記事で今朝私が出した奇抜なものに次を追加できます:codrspace.com/dexygen/… そして、つまり、キャッシュ無効化を追加する必要があるだけでなく、require.jsはハードリフレッシュを無視します。
Dexygen 2013

これの使用例について混乱しています...これはAMDモジュールをフロントエンドにホットリロードするためのものですか、それとも何ですか?
Alexander Mills

回答:


457

キャッシュ無効化のために、スクリプトの各URLに値を追加するようにRequireJSを構成できます。

RequireJSドキュメント(http://requirejs.org/docs/api.html#config)から:

urlArgs:RequireJSがリソースをフェッチするために使用するURLに追加される追加のクエリ文字列引数。ブラウザまたはサーバーが正しく構成されていない場合にバストをキャッシュするのに最も役立ちます。

例、すべてのスクリプトに「v2」を追加:

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

開発目的で、タイムスタンプを追加することにより、RequireJSに強制的にキャッシュをバイパスさせることができます。

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

46
とても助かります、ありがとう。urlArgs: "bust=" + (new Date()).getTime()開発中の自動キャッシュ無効urlArgs: "bust=v2"化と、更新された必要なスクリプトをロールアウトした後にハードコードされたバージョン番号を増分する本番環境で使用しています。
BumbleB2na 2011

9
...また、パフォーマンスオプティマイザとして、(new Date())。getTime()の代わりにMath.random()を使用できます。それはより美しく、オブジェクトを作成せず、少し高速なjsperf.com/speedcomparisonです。
Vlad Tsepelev 2013

2
あなたは少し小さく、それを得ることができます:urlArgs: "bust=" + (+new Date)
mrzmyr

11
キャッシュを毎回無効にするのはひどい考えだと思います。残念ながら、RequireJSには別の方法はありません。urlArgsは使用しますが、ランダムまたはタイムスタンプは使用しません。代わりに、現在のGit SHAを使用します。この方法は、新しいコードをデプロイしたときにのみ変更されます。
2014

5
「v2」文字列自体を提供するファイルがキャッシュされている場合、この「v2」バリアントはどのように運用されますか?新しいアプリケーションを本番環境にリリースした場合、「v3」を追加しても何も起こりません。これは、アプリケーションがv2の古い構成を含め、キャッシュされたv2ファイルを正常に処理し続けるためですurlArgs
ベニーボッテマ2015

54

これにはurlArgsを使用しないでください。

スクリプトのロードを要求すると、httpキャッシングヘッダーが尊重されます。(スクリプトは動的に挿入され<script>て読み込まれます。つまり、リクエストは古いアセットが読み込まれるように見えます。)

適切なHTTPヘッダーを使用してJavaScriptアセットを提供し、開発中のキャッシュを無効にします。

requireのurlArgsを使用すると、設定したブレークポイントは更新後も保持されません。debuggerコードのどこにでもステートメントを配置する必要があります。悪い。私urlArgsはgit sha を使用したプロダクションアップグレード中にキャッシュを無効にするアセットに使用します。次に、アセットを永久にキャッシュするように設定し、古くなったアセットがないことを保証できます。

開発では、すべてのajaxリクエストを複雑なmockjax構成でモックし、すべてのキャッシュをオフにした状態で、10行のpython httpサーバーを使用してjavascript専用モードでアプリを提供できます。これにより、何百ものWebサービスエンドポイントを備えた非常に大規模な「エンタープライズ」アプリケーションにスケールアップしました。バックエンドコードへのアクセスを許可せずに実際の製品コードベースで作業できる契約デザイナーもいます。


8
@ JamesP.Wright、(少なくともChromeでは)ページの読み込み時に発生する何かにブレークポイントを設定し、[更新]をクリックすると、URLが変更され、Chromeがブレークポイントをドロップしたため、ブレークポイントにヒットしません。これに対するクライアントのみの回避策を知りたいです。
Drew Noakes 2013年

1
ありがとう、ダスティン。これを回避する方法を見つけたら、投稿してください。それまではdebugger;、ブレークポイントを永続化させたい場所であればどこでもコードで使用できます。
BumbleB2na 2013年

2
ノードでhttp-serverを使用するすべての人(npm install http-server)。-c-1(つまり、http-server -c-1)でキャッシュを無効にすることもできます。
Yourpalal 2013

5
:あなたは、開発中のデバッグ問題を回避するためにChromeでキャッシュ無効にすることができstackoverflow.com/questions/5690269/...
ディーパック・ジョイ・

5
+1から!!!本番環境ではurlArgsを使用しないでください!!! 。あなたのWebサイトに1000個のJSファイルがあり(はい、可能です!)、その負荷はrequiredJSによって制御されているとします。これで、変更されたJSファイルがほとんどないv2またはサイトをリリースしました!しかし、urlArgs = v2を追加すると、すべての1000個のJSファイルを強制的に再ロードします。あなたは多くのトラフィックを支払うでしょう!変更されたファイルのみが再ロードされ、その他すべてはステータス304(未変更)で応答されます。
walv 14

24

urlArgsソリューションに問題があります。残念ながら、ユーザーとユーザーのWebブラウザーの間にある可能性があるすべてのプロキシサーバーを制御することはできません。これらのプロキシサーバーの一部は、残念ながら、ファイルをキャッシュするときにURLパラメータを無視するように設定できます。この場合、JSファイルの誤ったバージョンがユーザーに配信されます。

私はついに自分の修正をあきらめて実装しましたをあきらめてrequire.jsに直接しました。requirejsライブラリのバージョンを変更したい場合は、このソリューションが役立つかもしれません。

あなたはここでパッチを見ることができます:

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

追加したら、require configで次のようにすることができます。

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

ビルドシステムまたはサーバー環境を使用して置き換える buildNumberして、リビジョンID /ソフトウェアバージョン/好きな色。

次のようにrequireを使用します:

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

このファイルを要求する必要があります:

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

サーバー環境では、url書き換えルールを使用してbuildNumberを取り除き、正しいJSファイルを提供します。これにより、実際にすべてのJSファイルの名前を変更する必要がなくなります。

このパッチは、プロトコルを指定するスクリプトを無視し、JS以外のファイルには影響を与えません。

これは私の環境ではうまく機能しますが、一部のユーザーは接尾辞ではなく接頭辞を好むことに気づきます。ニーズに合わせてコミットを簡単に変更できるはずです。

更新:

プルリクエストの議論で、requirejsの作成者は、これがリビジョン番号の前に付ける解決策として機能する可能性があることを示唆しています。

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

私はこれを試していませんが、これは次のURLを要求することを意味します:

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

これは、プレフィックスを使用できる多くの人にとって非常にうまく機能する可能性があります。

重複する可能性のある質問は次のとおりです。

RequireJSとプロキシキャッシング

require.js-URLの一部として必要なモジュールにバージョンを設定するにはどうすればよいですか?


1
更新が公式のrequirejsビルドに反映されることを本当に望んでいます。requirejsの主な著者も興味があるかもしれません(上記の@Louisの回答を参照してください)。
BumbleB2na 2014

@ BumbleB2na-PullRequest(github.com/jrburke/requirejs/pull/1017)にコメントしてください。jrburke は興味を持っていなかったようです。彼はファイル名の接頭辞を使用した解決策を提案しています、私はそれを含めるように私の答えを更新します。
JBCP 2014

1
素敵なアップデート。私は著者によるこの提案が好きだと思いますが、それは私が最近この命名規則を使用しているためだけです:/scripts/myLib/v1.1/。私はファイル名にpostfix(またはプレフィックス)を追加しようとしましたが、それはおそらくjqueryが行うためですが、しばらくしてから、[怠惰になって]親フォルダーのバージョン番号をインクリメントし始めました。大規模なWebサイトでのメンテナンスが容易になったと思いますが、今ではURL書き換えの悪夢について心配しています。
BumbleB2na 2014

1
最新のフロントエンドシステムは、JSファイルを書き換え、ファイル名にMD5サムを付けた後、HTMLファイルを書き換えて、ビルド時に新しいファイル名を使用しますが、フロントエンドコードがサーバー側で提供されるレガシーシステムでは扱いが難しくなります。
JBCP 2014

これは、jspxファイル内にいくつかのjsが必要なときに機能しますか?このように<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.js data-mainのExpireキャッシュに触発されて、次のantタスクでデプロイスクリプトを更新しました。

<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の先頭は次のようになります。

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

11

生産中

urlArgs 問題を引き起こす可能性があります!

requirejsの主な作者は以下を使用しないことを好みますurlArgs

デプロイされたアセットの場合、私はビルド全体のバージョンまたはハッシュをビルドディレクトリとして配置baseUrlし、プロジェクトで使用されている構成を変更して、バージョン管理されたディレクトリをとして使用することを好みbaseUrlます。その後、他のファイルは変更されず、クエリ文字列を含むURLをキャッシュしない可能性があるプロキシの問題を回避するのに役立ちます。

[私のスタイリング。]

私はこのアドバイスに従います。

開発中

サーバーその発する:私はインテリジェントに頻繁に変更される可能性のあるファイルをキャッシュサーバー使用することを好むLast-Modifiedのと応答If-Modified-Sinceで304適切に。静的ファイルを提供するNodeのExpressセットに基づくサーバーでさえ、すぐにこれを実行します。それは私のブラウザーに何もする必要がなく、ブレークポイントを台無しにすることはありません。


良い点ですが、答えはサーバー環境に固有です。たまたまこれに遭遇した他の誰にとっても良い選択肢は、クエリ文字列パラメータの代わりにファイル名にバージョン番号を追加するための最近の推奨事項です。そのテーマに関する詳細は次のとおり
blog

本番システムでこの特定の問題が発生しています。パラメータを使用するよりも、ファイル名を変更することをお勧めします。
JBCP 2014

7

このスニペットをAskApacheから取得して、ローカルのApacheウェブサーバーの別個の.confファイル(私の場合は/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>

開発の場合、これはコードを変更する必要なく問題なく機能します。制作に関しては、@ dvtoeverのアプローチを使用する場合があります。


6

開発のためのクイックフィックス

開発の場合は、Chrome Dev Toolsでキャッシュを無効にするだけです(ウェブサイト開発用のChromeキャッシュを無効にする)。キャッシュの無効化は、開発ツールダイアログが開いている場合にのみ行われるため、通常のブラウジングを行うたびにこのオプションを切り替えることを心配する必要はありません。

注: ' urlArgs 'を使用することは、ユーザーが最新のコードを取得できるようにするための適切なソリューションです。ただし、Chromeでは更新のたびにブレークポイントが無効になるため、デバッグが難しくなります(そのたびに「新しい」ファイルが提供されるため)。


3

' urlArgsの使用はお勧めしませんのキャッシュバーストに」を。これは問題を完全に解決しないので。バージョンを更新しないと、単一のリソースを変更しただけでも、すべてのリソースがダウンロードされます。

この問題を処理するには、リビジョン番号を作成するために 'filerev'のようなGruntモジュールを使用することをお勧めします。さらに、Gruntfileでカスタムタスクを作成し、必要な場所でリビジョンを更新していません。

必要に応じて、このタスクのコードスニペットを共有できます。


Javascriptファイルを書き換えるためにgrunt-filerevとgrunt-cache-busterの組み合わせを使用しています。
Ian Jamieson、2015

2

これは私がDjango / Flaskでそれを行う方法です(他の言語/ VCSシステムに簡単に適応させることができます):

あなたの中でconfig.py(私はこれをpython3で使用しているので、python2のエンコーディングを微調整する必要があるかもしれません)

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

次に、テンプレートで:

{% if config.DEBUG %}
     require.config({urlArgs: "bust=" + (new Date().getTime())});
{% else %}
    require.config({urlArgs: "bust=" + {{ config.GIT_HASH|tojson }}});
{% endif %}
  • 手動でのビルドプロセスは不要
  • git rev-parse HEADアプリの起動時に一度だけ実行され、それをconfigオブジェクトに保存します

0

動的ソリューション(urlArgsなし)

この問題の簡単な解決策があるため、すべてのモジュールに一意のリビジョン番号をロードできます。

元のrequirejs.load関数を保存して独自の関数で上書きし、変更したURLを解析して元のrequirejs.loadに再度変換できます。

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

私たちのビルドプロセスでは、「gulp-rev」を使用して、使用されているすべてのモジュールのすべてのリビジョンを含むマニフェストファイルをビルドしました。私のgulpタスクの簡略化されたバージョン:

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

これにより、main.jsに 'oRevision'として含まれるmoduleNamesのリビジョン番号を持つAMDモジュールが生成されます。この場合、前に示したようにrequirejs.load関数を上書きします。


-1

これは、@ phil mccullが受け入れた回答に追加されます。

私は彼の方法を使用しますが、ビルド前に実行するT4テンプレートを作成してプロセスを自動化します。

ビルド前のコマンド:

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"

ここに画像の説明を入力してください

T4テンプレート:

ここに画像の説明を入力してください

生成されたファイル: ここに画像の説明を入力してください

require.config.jsがロードされる前に変数に保存します: ここに画像の説明を入力してください

require.config.jsでの参照:

ここに画像の説明を入力してください


2
JavaScript問題の解決策がC#コードの束であるためと考えられます。これは、受け入れられた回答とまったく同じ方法で最終的に実行される処理を実行するための追加の作業とコードの集まりでもあります。
mAAdhaTTah 2016

@mAAdhaTTahこれは任意のサーバー側言語で実行できます... c#である必要はありません。また、これによりプロセスが自動化され、プロジェクトの新しいバージョンをビルドするときにキャッシュバストが更新されるので、顧客は常にスクリプトの最新バージョンをキャッシュできます。私はそれが否定的な値下げに値するとは思わない。それは、ソリューションへの自動化されたアプローチを提供することによって、答えを広げているだけです。
ザックペインター

require.jsを使用するアプリケーションを維持するときに、「(new Date())。getTime())を手動でコメントアウトし、アプリケーションを更新するたびに静的キャッシュバスターのコメントを外す必要があるので、かなり面倒だと思ったので、基本的にこれを作成しました。簡単には忘れて、彼らは何も変わっていないと思いますので、すべての突然のお客様が変更内容を検証し、キャッシュされたスクリプトを見ている..あなたは、単にキャッシュ対策を変更するのを忘れているのですべては余分なコードのこの小さなビットは、その出来事のチャンスを消去します。。。
ザックペインター

2
私はそれを書き留めませんでした、私は実際に何が起こったのかわかりません。jsだけでなく、C#テンプレート言語でもないコードは、JS開発者が問題の答えを得ようとするのにそれほど役立ちません。
mAAdhaTTah 2016

-2

私の場合、クリックするたびに同じフォームをロードしたかったのですが、ファイルに加えた変更を保持したくありませんでした。これはこの投稿とは正確には関係ないかもしれませんが、必要に応じてconfigを設定しないと、クライアント側で解決策になる可能性があります。コンテンツを直接送信する代わりに、必要なファイルのコピーを作成して、実際のファイルをそのまま保持できます。

LoadFile(filePath){
    const file = require(filePath);
    const result = angular.copy(file);
    return result;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.