RequireJS가 필수 스크립트를 캐시하지 못하도록 방지


302

RequireJS는 필요한 자바 스크립트 파일을 캐시하는 내부 작업을 수행하는 것 같습니다. 필요한 파일 중 하나를 변경하면 변경 사항이 적용되도록 파일 이름을 바꿔야합니다.

파일 이름 끝에 querystring 매개 변수로 버전 번호를 추가하는 일반적인 트릭은 requirejs에서 작동하지 않습니다. <script src="jsfile.js?v2"></script>

내가 찾고있는 것은 업데이트 할 때마다 스크립트 파일의 이름을 바꾸지 않고도 RequireJS 필수 스크립트의 내부 캐시를 방지하는 방법입니다.

플랫폼 간 솔루션 :

내가 지금 사용하고 있습니다 urlArgs: "bust=" + (new Date()).getTime()에 대한 자동 캐시 무효화 개발 과정과 urlArgs: "bust=v2"내가 업데이트에 필요한 스크립트를 출시 한 후 하드 코딩 된 버전 납입을 증가 생산.

노트 :

@Dustin Getz는 최근 답변에서 Javascript 파일을 이와 같이 지속적으로 새로 고칠 때 Chrome 개발자 도구가 디버깅 중에 중단 점을 제거한다고 언급했습니다. 한 가지 해결 방법은 debugger;대부분의 Javascript 디버거에서 중단 점을 트리거하기 위해 코드 를 작성 하는 것입니다.

서버 별 솔루션 :

노드 또는 Apache와 같은 서버 환경에 더 적합한 특정 솔루션에 대해서는 아래 답변 중 일부를 참조하십시오.


이것이 캐싱을 수행하는 서버 또는 클라이언트가 아닌 것입니까? (몇 달 동안 필요한 js를 사용하고 비슷한 것을 보지 못했습니다.) IE는 MVC 작업 결과를 캐싱하고 크롬은 HTML 템플릿을 캐싱했지만 브라우저 캐시가 재설정되면 js 파일이 모두 새로 고쳐지는 것처럼 보입니다. 캐싱을 사용하려고하지만 필요한 j의 요청이 문제를 일으킬 수있는 쿼리 문자열을 제거했기 때문에 평소에는 할 수 없다고 생각합니다.
PJUK

RequireJS가 추가 된 버전 번호를 제거하는지 확실하지 않습니다. 내 서버 일 수도 있습니다. RequireJS가 캐시 버스터 설정을 갖는 방법에 흥미가 있으므로 필요한 파일에서 추가 된 버전 번호를 제거하는 것이 옳을 수도 있습니다.
BumbleB2na

잠재적 인 캐싱 솔루션으로 내 대답을 업데이트했습니다
Dustin Getz

이제 오늘 아침 내 블로그 게시물에 게시 한 내용에 다음을 추가 할 수 있습니다. codrspace.com/dexygen/… 즉, 캐시 버스 팅을 추가해야 할뿐만 아니라 require.js는 하드 새로 고침을 무시합니다.
Dexygen

유스 케이스에 대해 혼란스러워합니다 .... 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

9
... 또한 성능 최적화 프로그램으로 (new Date ()). getTime () 대신 Math.random ()을 사용할 수 있습니다. 더 아름답고 객체를 만들지 않고 조금 더 빠른 jsperf.com/speedcomparison .
Vlad Tsepelev

2
조금 더 작아도됩니다.urlArgs: "bust=" + (+new Date)
mrzmyr

11
매번 캐시를 파열시키는 것은 끔찍한 생각이라고 생각합니다. 불행히도 RequireJS는 다른 대안을 제공하지 않습니다. 우리는 urlArgs를 사용하지만 이것에 무작위 또는 타임 스탬프를 사용하지 않습니다. 대신 우리는 현재 Git SHA를 사용합니다. 새로운 코드를 배포 할 때만 변경됩니다.
Ivan Torres

5
"v2"문자열 자체를 제공하는 파일이 캐시 된 경우 프로덕션에서이 "v2"변형이 어떻게 작동합니까? 새 응용 프로그램을 프로덕션에 출시하면 "v3"을 추가해도 아무 것도 수행되지 않습니다. 응용 프로그램은 v2의 이전 구성을 포함하여 캐시 된 v2 파일을 계속 사용하기 때문입니다 urlArgs.
베니 보 테마

54

urlArgs를 사용하지 마십시오!

스크립트로드에는 http 캐싱 헤더가 필요합니다. 스크립트는 동적으로 삽입 된 상태로로드됩니다. <script>즉, 요청이로드 된 기존 애셋처럼 보입니다.

개발 중에 캐싱을 비활성화하려면 적절한 HTTP 헤더를 사용하여 Javascript 자산을 제공하십시오.

require의 urlArgs를 사용하면 설정 한 중단 점이 새로 고침 동안 유지되지 않습니다. 결국 debugger코드 어디에나 명령문 을 넣어야 합니다. 나쁜. urlArgsgit sha를 사용하여 프로덕션 업그레이드 중에 캐시 버스 팅 자산에 사용 합니다. 그런 다음 자산을 영구적으로 캐시하도록 설정하고 오래된 자산을 보유하지 않도록 보장 할 수 있습니다.

개발 과정에서 복잡한 mockjax 구성으로 모든 ajax 요청을 조롱 한 다음 모든 캐싱이 해제10 줄 파이썬 http 서버로 자바 스크립트 전용 모드로 앱을 제공 할 수 있습니다 . 이것은 수백 개의 편안한 웹 서비스 엔드 포인트가있는 상당히 큰 "엔터프라이즈"응용 프로그램으로 확장되었습니다. 백엔드 코드에 액세스하지 않고도 실제 프로덕션 코드베이스로 작업 할 수있는 계약 된 디자이너도 있습니다.


8
@ JamesP.Wright, 페이지로드시 발생하는 중단 점을 설정 한 후 새로 고침을 클릭하면 URL이 변경되고 Chrome이 중단 점을 삭제했기 때문에 중단 점에 도달하지 않기 때문에 (최소한 Chrome에서는) @ JamesP.Wright입니다. 이에 대한 클라이언트 전용 해결 방법을 알고 싶습니다.
Drew Noakes 2014 년

1
고마워요, 더스틴 이 문제를 해결할 방법을 찾으면 게시하십시오. 그 동안 debugger;중단 점을 유지하려는 모든 위치에서 코드에서 사용할 수 있습니다 .
BumbleB2na

2
노드에서 http-server를 사용하는 사람 (npm install http-server). -c-1을 사용하여 캐싱을 비활성화 할 수도 있습니다 (예 : http-server -c-1).
Yourpalal

5
개발 중에 디버깅 문제를 해결하기 위해 Chrome에서 캐싱을 비활성화 할 수 있습니다. stackoverflow.com/questions/5690269/…
Deepak Joy

5
+1 ~ !!! urlArgs를 생산에 사용하지 마십시오 !!! . 웹 사이트에 1000 개의 JS 파일 (예, 가능!)이 있고로드가 requiredJS에 의해 제어되는 경우를 상상해보십시오. 이제 JS 파일 만 변경되는 v2 또는 사이트를 릴리스했습니다! 그러나 urlArgs = v2를 추가하면 1000 개의 JS 파일을 모두 다시로드해야합니다! 당신은 많은 트래픽을 지불합니다! 수정 된 파일 만 다시로드해야하며 다른 모든 파일은 상태 304 (수정되지 않음)로 응답해야합니다.
walv

24

urlArgs 솔루션에 문제가 있습니다. 불행히도 귀하와 사용자의 웹 브라우저 사이에있을 수있는 모든 프록시 서버를 제어 할 수는 없습니다. 이러한 프록시 서버 중 일부는 파일 캐싱시 URL 매개 변수를 무시하도록 구성 할 수 있습니다. 이 경우 잘못된 버전의 JS 파일이 사용자에게 전달됩니다.

나는 마침내 내 자신의 수정 사항 을 require.js에 직접 포기하고 구현했습니다 . requirejs 라이브러리의 버전을 기꺼이 수정하려는 경우이 솔루션이 적합 할 수 있습니다.

여기서 패치를 볼 수 있습니다.

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

추가 한 후에는 require 설정에서 다음과 같은 작업을 수행 할 수 있습니다.

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

빌드 시스템 또는 서버 환경을 buildNumber사용하여 개정 ID / 소프트웨어 버전 / 좋아하는 색상 으로 바꾸십시오 .

다음과 같이 요구합니다.

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

접두사를 사용할 수있는 많은 사람들에게 매우 효과적 일 수 있습니다.

가능한 중복 질문은 다음과 같습니다.

JS 및 프록시 캐싱

require.js-URL의 일부로 필요한 모듈의 버전을 어떻게 설정할 수 있습니까?


1
업데이트가 공식 requirejs 빌드로 들어가는 것을 정말로보고 싶습니다. requirejs의 주요 저자도 관심이있을 수 있습니다 (위의 @Louis의 답변 참조).
BumbleB2na 2019

@ BumbleB2na-PullRequest ( github.com/jrburke/requirejs/pull/1017 ) 에 대해 자유롭게 의견을 말하십시오 .jrburke 는 관심이 없었습니다. 그는 파일 이름 접두사를 사용하는 솔루션을 제안합니다. 그것을 포함하도록 답변을 업데이트하겠습니다.
JBCP

1
좋은 업데이트입니다. 필자는 저자의 제안을 좋아한다고 생각하지만, 최근에이 명명 규칙을 사용했기 때문 /scripts/myLib/v1.1/입니다. 필자는 아마도 jquery가하는 일이기 때문에 파일 이름에 접미사 (또는 접두사)를 추가하려고 시도했지만 잠시 후에 부모 폴더에서 버전 번호가 증가하기 시작했습니다. 큰 웹 사이트에서 유지 관리가 쉬워 졌다고 생각하지만 URL 재 작성 악몽에 대해 걱정하고 있습니다.
BumbleB2na

1
최신 프론트 엔드 시스템은 JS 파일과 파일 이름에 MD5 합계를 다시 쓴 다음 HTML 파일을 다시 작성하여 새 파일 이름을 사용하여 작성하지만 프런트 엔드 코드가 서버 측에서 제공되는 레거시 시스템에서는 까다로워집니다.
JBCP

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 캐시에서 영감을 받아 다음과 같은 개미 작업으로 배포 스크립트를 업데이트했습니다.

<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을 캐시하지 않을 수있는 프록시 문제를 피할 수 있습니다.

[스타일링 광산.]

나는이 조언을 따릅니다.

개발 중

자주 변경 될 수있는 파일을 지능적으로 캐시하는 서버를 사용하는 것이 좋습니다. 적절한 경우 304로 내보내고 Last-Modified응답 하는 서버입니다 If-Modified-Since. 정적 파일을 제공하기 위해 Node의 Express 세트를 기반으로 한 서버조차도 즉시 사용할 수 있습니다. 브라우저에 아무것도 할 필요가 없으며 중단 점을 망칠 필요가 없습니다.


좋은 점이지만 귀하의 답변은 서버 환경에 따라 다릅니다. 아마도이 문제를 겪고있는 다른 사람들을위한 좋은 대안은 쿼리 문자열 매개 변수 대신 파일 이름에 버전 번호를 추가하는 최근 권장 사항입니다. 다음은 해당 주제에 대한 자세한 정보입니다. stevesouders.com/blog/2008/08/23/…
BumbleB2na

프로덕션 시스템에서이 특정 문제가 발생했습니다. 매개 변수를 사용하는 대신 파일 이름을 수정하는 것이 좋습니다.
JBCP

7

AskApache 에서이 스 니펫을 가져 와서 로컬 Apache 웹 서버 (내 경우에는 /etc/apache2/others/preventcaching.conf)의 별도 .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 개발자 도구에서 캐시를 사용 중지 할 수 있습니다 ( 웹 사이트 개발을 위해 Chrome 캐시 사용 안함 ). 캐시 비활성화는 개발 도구 대화 상자가 열려있는 경우에만 발생하므로 정기적으로 탐색 할 때마다이 옵션을 전환 할 염려가 없습니다.

참고 : ' urlArgs '를 사용하면 프로덕션 환경에서 사용자가 최신 코드를 얻을 수있는 올바른 솔루션입니다. 그러나 크롬은 매번 새로 고침 할 때마다 새로 고침 할 때마다 중단 점이 무효화되므로 디버깅이 어려워집니다.


3

RequireJS 에서 캐시 버스 팅에 ' urlArgs '를 사용하지 않는 것이 좋습니다 . 이렇게해도 문제가 완전히 해결되지는 않습니다. no 버전을 업데이트하면 단일 리소스 만 변경하더라도 모든 리소스를 다운로드하게됩니다.

이 문제를 해결하려면 개정 번호를 만들 때 'filerev'와 같은 Grunt 모듈을 사용하는 것이 좋습니다. 이 외에도 Gruntfile에서 사용자 정의 작업을 작성하여 필요한 곳이없는 개정을 업데이트했습니다.

필요한 경우이 작업에 대한 코드 스 니펫을 공유 할 수 있습니다.


grunt-filerev와 grunt-cache-buster의 조합을 사용하여 Javascript 파일을 다시 씁니다.
Ian Jamieson

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

@mAAdhaTTah 모든 서버 측 언어 로이 작업을 수행 할 수 있습니다 ... c # 일 필요는 없습니다. 또한 이것은 프로세스를 자동화하여 고객이 항상 최신 버전의 스크립트를 캐싱하도록 새 버전의 프로젝트를 빌드 할 때 캐시 버스트를 업데이트합니다. 나는 그것이 부정적인 마크 다운을받을 가치가 있다고 생각하지 않습니다. 솔루션에 자동화 된 접근 방식을 제공함으로써 답변을 확장하고 있습니다.
Zach Painter

require.js를 사용하는 응용 프로그램을 유지 관리 할 때 응용 프로그램을 업데이트 할 때마다 "(new Date ()). getTime ())을 수동으로 주석 처리하고 정적 캐시 버스터의 주석 처리를 제거해야하기 때문에 기본적으로 이것을 만들었습니다. 잊어 버리기 쉬운 순간 갑자기 고객이 변경 사항을 확인하고 캐시 된 스크립트를보고 아무것도 변경되지 않았다고 생각하는 경우 .. 단순히 캐시 버스터를 변경하는 것을 잊어 버렸기 때문에이 작은 코드는 약간의 추가 코드가 발생할 가능성을 없애줍니다.
Zach Painter

2
나는 그것을 표시하지 않았다, 나는 실제로 무슨 일이 있었는지 모른다. js뿐만 아니라 C # 템플릿 언어 인 코드를 제안하면 JS 개발자가 문제에 대한 답을 얻으려고 노력하는 데 도움이되지 않습니다.
mAAdhaTTah

-2

필자의 경우 클릭 할 때마다 동일한 양식을로드하고 싶었지만 파일에서 변경 한 내용을 유지하고 싶지 않았습니다. 이 게시물과 정확히 관련이 없을 수도 있지만, config를 require로 설정하지 않고 클라이언트 쪽에서 가능한 해결책이 될 수 있습니다. 내용을 직접 보내지 않고 필요한 파일을 복사하여 실제 파일을 그대로 유지할 수 있습니다.

LoadFile(filePath){
    const file = require(filePath);
    const result = angular.copy(file);
    return result;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.