web-dev-qa-db-ja.com

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

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

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

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

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

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

注:

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

サーバー固有の解決策

NodeやApacheなど、ご使用のサーバー環境に適した特定の解決策については、以下の回答を参照してください。

293
BumbleB2na

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

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()
});
448
phil mccullick

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

[スクリプトのロードを要求する]では、HTTPキャッシュヘッダーが考慮されます。 (スクリプトは動的に挿入された<script>でロードされます。つまり、リクエストは古いアセットがロードされたように見えます。)

開発中のキャッシュを無効にするために、適切なHTTPヘッダーを使用してJavaScript資産を処理してください。

RequireのurlArgsを使用すると、設定したブレークポイントは更新されても保持されません。あなたは結局あなたのコードのどこにでもdebuggerステートメントを置く必要があるのです。悪い。私はgit shaを使ってプロダクションをアップグレードする際にキャッシュを破壊するアセットにurlArgsを使います。それから私は自分の資産を永遠にキャッシュされるように設定することができ、古い資産を決して持たないことが保証されます。

開発中、私はすべてのajaxリクエストを複雑な mockjax 構成でモックし、それから 10行のpython httpを使ってJavascriptのみのモードでアプリを提供することができます。すべてのキャッシュを持つサーバーはオフになりました 。私にとってこれは、何百もの安静なWebサービスエンドポイントを持つ非常に大規模な「エンタープライズ」アプリケーションにまで拡大しました。バックエンドのコードにアクセスすることなく、実際のプロダクションコードベースを扱うことができる契約デザイナーもいます。

54
Dustin Getz

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

私たちのサーバー環境では、私たちはbuildNumberを取り除き、正しいJSファイルを提供するためにurlリライトルールを使います。こうすれば、実際にはすべてのJSファイルの名前を変更する必要はありません。

この更新プログラムはプロトコルを指定するスクリプトを無視し、JS以外のファイルには影響しません。

これは私の環境ではうまくいきますが、一部のユーザーは接尾辞よりも接頭辞を好むことを実感します。あなたのニーズに合うように私のコミットを変更するのは簡単なはずです。

更新:

Pull requestの議論の中で、requirejsの作者はこれがリビジョン番号を前に付けるための解決策として働くかもしれないと提案します:

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

私はこれを試したことはありませんが、含意はこれが次のURLを要求するということです。

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

これは、プレフィックスを使うことができる多くの人にとって非常にうまくいくでしょう。

ここにいくつかの可能な重複質問があります:

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

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

22
JBCP

require.js data-mainのキャッシュの有効期限切れ からヒントを得た==次のantタスクでdeployスクリプトを更新しました。

<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@',
    ...
});
19
dvtoever

生産中

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

Requirejsの主な作者 urlArgsを使わないことを好む

デプロイされたアセットの場合は、ビルド全体のバージョンまたはハッシュをビルドディレクトリとして置き、そのバージョン管理ディレクトリをbaseUrlとして使用するようにプロジェクトに使用されているbaseUrl設定を変更するだけです。そうすれば他のファイルは変更されず、回避するのに役立ちますプロキシ文字列にクエリ文字列を含むURLがキャッシュされない場合があるという問題がいくつかあります。

[スタイリング鉱山]

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

開発中

頻繁に変更される可能性があるファイルをインテリジェントにキャッシュするサーバーを使用することをお勧めします。必要に応じて、Last-Modifiedを発行し、304でIf-Modified-Sinceに応答するサーバーです。ノードの express 静的ファイルを処理するように設定されているサーバーでも、すぐにこれが可能です。私のブラウザに何かをする必要はなく、ブレークポイントを乱すこともありません。

11
Louis

このスニペットを AskApache から取り出して、私のローカルのApache Webサーバーの別の.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のアプローチを使うかもしれません。

7
myrho

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

開発には、Chrome Dev Toolsでキャッシュを無効にするWebサイト開発のためにChromeキャッシュを無効にする )と指定することもできます。キャッシュの無効化は開発ツールのダイアログが開いている場合にのみ発生するので、通常の閲覧をするたびにこのオプションを切り替えることについて心配する必要はありません。

注: 'rlArgs'を使用することは、ユーザーが最新のコードを入手できるように、本番環境では適切な解決策です。しかし、chromeは毎回更新されるたびにブレークポイントを無効にするのでデバッグが難しくなります(そのたびに '新しい'ファイルが提供されるためです)。

5
Deepak Joy

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

この問題に対処するには、リビジョン番号を作成するために 'filerev'のようなGruntモジュールを使用することをお勧めします。これに加えて、私はGruntfileにカスタムタスクを書いてリビジョンを更新する必要はありません。

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

3
Amit Sagar

これは私が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オブジェクトに格納します。
2
Stephen Fuhry

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

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

0
mat