programing

Angular 2 사이트에서 브라우저 캐시를 방지하는 방법은 무엇입니까?

bestprogram 2023. 5. 27. 12:04

Angular 2 사이트에서 브라우저 캐시를 방지하는 방법은 무엇입니까?

우리는 현재 고객 중 한 명이 매일 사용하는 정기적인 업데이트를 통해 새로운 프로젝트를 진행하고 있습니다.이 프로젝트는 Angular 2를 사용하여 개발되고 있으며 캐시 문제에 직면해 있습니다. 즉, 고객이 최신 기계 변경 사항을 확인하지 못하고 있습니다.

주로 js 파일의 html/css 파일은 큰 문제 없이 제대로 업데이트되는 것 같습니다.

angular-cli는 다음을 제공하여 이 문제를 해결합니다.--output-hashingflag for build 명령(6/7 버전 추가, 이후 버전은 여기 참조).사용 예:

ng build --output-hashing=all

번들링 & 트리 쉐이킹은 몇 가지 세부 사항과 맥락을 제공합니다.입니다.ng help build플래그를 문서화합니다.

--output-hashing=none|all|media|bundles (String)

Define the output filename cache-busting hashing mode.
aliases: -oh <value>, --outputHashing <value>

이것은 angular-cli 사용자에게만 적용 가능하지만, 훌륭하게 작동하며 코드 변경이나 추가 툴링이 필요하지 않습니다.

갱신하다

많은 의견들이 이 답변이 해시를 추가한다는 것을 유용하고 정확하게 지적했습니다..js파일이지만 아무 작업도 수행하지 않습니다.index.html그러므로 그것은 전적으로 가능합니다.index.html캐된상유는시간되지로태 뒤에 상태로 ng build가 시가고장을 합니다..jsfiles 파일

이제 모든 브라우저에서 페이지 캐싱을 제어하는 방법에 대해 설명하겠습니다.

이를 위한 방법을 찾았습니다. 다음과 같이 쿼리 문자열을 추가하여 구성 요소를 로드하기만 하면 됩니다.

@Component({
  selector: 'some-component',
  templateUrl: `./app/component/stuff/component.html?v=${new Date().getTime()}`,
  styleUrls: [`./app/component/stuff/component.css?v=${new Date().getTime()}`]
})

이렇게 하면 클라이언트는 브라우저의 템플리트 대신 서버의 템플리트 사본을 로드해야 합니다.일정 시간이 지난 후에만 새로 고치려면 이 ISO 문자열을 대신 사용할 수 있습니다.

new Date().toISOString() //2016-09-24T00:43:21.584Z

그리고 일부 문자를 부분 문자열로 변환하여 다음과 같이 한 시간 후에만 변경할 수 있습니다.

new Date().toISOString().substr(0,13) //2016-09-24T00

이것이 도움이 되길 바랍니다.

각 html 템플릿에서 맨 위에 다음 메타 태그를 추가합니다.

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

내가 알기로는 각 템플릿은 독립적이므로 index.html 파일에 설정된 메타 no 캐싱 규칙을 상속하지 않습니다.

@Jack의 대답과 @ranierbit의 대답을 조합하면 효과가 있을 것입니다.

--output-hashing에 대해 ng 빌드 플래그를 설정합니다.

ng build --output-hashing=all

다음 이 사용자의 합니다.app.module

@Injectable()
export class NoCacheHeadersInterceptor implements HttpInterceptor {
    intercept(req: HttpRequest<any>, next: HttpHandler) {
        const authReq = req.clone({
            setHeaders: {
                'Cache-Control': 'no-cache',
                 Pragma: 'no-cache'
            }
        });
        return next.handle(authReq);    
    }
}

다음 런그 의 에게 이 합니다.app.module:

providers: [
  ... // other providers
  {
    provide: HTTP_INTERCEPTORS,
    useClass: NoCacheHeadersInterceptor,
    multi: true
  },
  ... // other providers
]

이렇게 하면 클라이언트 컴퓨터의 라이브 사이트에서 캐싱 문제가 발생하지 않습니다.

nginx에 추가합니다.

location ~ /index.html|.*\.json$ {
        expires -1;
        add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
    }

location ~ .*\.css$|.*\.js$ {
   add_header Cache-Control 'max-age=31449600'; # one year
}

location / {
    try_files $uri $uri/ /index.html?$args;
    add_header Cache-Control 'max-age=86400'; # one day
}

브라우저에서 index.html을 캐시하는 것과 유사한 문제가 있거나 중간 cdn/proxies에서 더 까다롭게 문제가 있었습니다(F5는 당신에게 도움이 되지 않습니다.

저는 고객이 최신 index.html 버전을 가지고 있는지 100% 확인하는 솔루션을 찾았습니다. 운 좋게도 Henrik Peinar의 솔루션을 찾았습니다.

https://blog.nodeswat.com/automagic-reload-for-clients-after-deploy-with-angular-4-8440c9fdd96c

이 솔루션은 클라이언트가 브라우저를 연 상태로 며칠 동안 유지되고 클라이언트가 업데이트를 주기적으로 확인하며 최신 버전이 배포된 경우 다시 로드하는 경우도 해결합니다.

솔루션은 다소 까다롭지만 매력적으로 작동합니다.

  • 라는 합니다.ng cli -- prod는 mainmain이라는 합니다.]js[계s]js
  • 해당 해시가 포함된 version.json 파일을 만듭니다.
  • version.json을 확인하고 필요한 경우 다시 로드하는 Angular 서비스 VersionCheckService를 만듭니다.
  • 배포 후 실행되는 js 스크립트는 version.json을 생성하고 각 서비스에서 해시를 교체하므로 수동 작업이 필요하지 않지만 post-build.js를 실행합니다.

Henrik Peinar 솔루션은 각도 4에 대한 솔루션이었기 때문에 약간의 변경 사항이 있었습니다. 여기에 고정 스크립트도 배치했습니다.

버전 확인 서비스:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class VersionCheckService {
    // this will be replaced by actual hash post-build.js
    private currentHash = '{{POST_BUILD_ENTERS_HASH_HERE}}';

    constructor(private http: HttpClient) {}

    /**
     * Checks in every set frequency the version of frontend application
     * @param url
     * @param {number} frequency - in milliseconds, defaults to 30 minutes
     */
    public initVersionCheck(url, frequency = 1000 * 60 * 30) {
        //check for first time
        this.checkVersion(url); 

        setInterval(() => {
            this.checkVersion(url);
        }, frequency);
    }

    /**
     * Will do the call and check if the hash has changed or not
     * @param url
     */
    private checkVersion(url) {
        // timestamp these requests to invalidate caches
        this.http.get(url + '?t=' + new Date().getTime())
            .subscribe(
                (response: any) => {
                    const hash = response.hash;
                    const hashChanged = this.hasHashChanged(this.currentHash, hash);

                    // If new version, do something
                    if (hashChanged) {
                        // ENTER YOUR CODE TO DO SOMETHING UPON VERSION CHANGE
                        // for an example: location.reload();
                        // or to ensure cdn miss: window.location.replace(window.location.href + '?rand=' + Math.random());
                    }
                    // store the new hash so we wouldn't trigger versionChange again
                    // only necessary in case you did not force refresh
                    this.currentHash = hash;
                },
                (err) => {
                    console.error(err, 'Could not get version');
                }
            );
    }

    /**
     * Checks if hash has changed.
     * This file has the JS hash, if it is a different one than in the version.json
     * we are dealing with version change
     * @param currentHash
     * @param newHash
     * @returns {boolean}
     */
    private hasHashChanged(currentHash, newHash) {
        if (!currentHash || currentHash === '{{POST_BUILD_ENTERS_HASH_HERE}}') {
            return false;
        }

        return currentHash !== newHash;
    }
}

메인 AppComponent로 변경:

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
    constructor(private versionCheckService: VersionCheckService) {

    }

    ngOnInit() {
        console.log('AppComponent.ngOnInit() environment.versionCheckUrl=' + environment.versionCheckUrl);
        if (environment.versionCheckUrl) {
            this.versionCheckService.initVersionCheck(environment.versionCheckUrl);
        }
    }

}

마법을 만드는 빌드 후 스크립트, post-build.js:

const path = require('path');
const fs = require('fs');
const util = require('util');

// get application version from package.json
const appVersion = require('../package.json').version;

// promisify core API's
const readDir = util.promisify(fs.readdir);
const writeFile = util.promisify(fs.writeFile);
const readFile = util.promisify(fs.readFile);

console.log('\nRunning post-build tasks');

// our version.json will be in the dist folder
const versionFilePath = path.join(__dirname + '/../dist/version.json');

let mainHash = '';
let mainBundleFile = '';

// RegExp to find main.bundle.js, even if it doesn't include a hash in it's name (dev build)
let mainBundleRegexp = /^main.?([a-z0-9]*)?.js$/;

// read the dist folder files and find the one we're looking for
readDir(path.join(__dirname, '../dist/'))
  .then(files => {
    mainBundleFile = files.find(f => mainBundleRegexp.test(f));

    if (mainBundleFile) {
      let matchHash = mainBundleFile.match(mainBundleRegexp);

      // if it has a hash in it's name, mark it down
      if (matchHash.length > 1 && !!matchHash[1]) {
        mainHash = matchHash[1];
      }
    }

    console.log(`Writing version and hash to ${versionFilePath}`);

    // write current version and hash into the version.json file
    const src = `{"version": "${appVersion}", "hash": "${mainHash}"}`;
    return writeFile(versionFilePath, src);
  }).then(() => {
    // main bundle file not found, dev build?
    if (!mainBundleFile) {
      return;
    }

    console.log(`Replacing hash in the ${mainBundleFile}`);

    // replace hash placeholder in our main.js file so the code knows it's current hash
    const mainFilepath = path.join(__dirname, '../dist/', mainBundleFile);
    return readFile(mainFilepath, 'utf8')
      .then(mainFileData => {
        const replacedFile = mainFileData.replace('{{POST_BUILD_ENTERS_HASH_HERE}}', mainHash);
        return writeFile(mainFilepath, replacedFile);
      });
  }).catch(err => {
    console.log('Error with post build:', err);
  });

스크립트를 (새) 빌드 폴더에 배치하기만 하면 스크립트 실행node ./build/post-build.js를 사용하여 dist 한 후ng build --prod

HTTP 헤더를 사용하여 클라이언트 캐시를 제어할 수 있습니다.이것은 모든 웹 프레임워크에서 작동합니다.

캐시를 활성화하는 방법과 시기를 세부적으로 제어하도록 이러한 헤더의 지시를 설정할 수 있습니다.

  • Cache-Control
  • Surrogate-Control
  • Expires
  • ETag 사람 (우은좋것매것))▁(좋(
  • Pragma 브라우저를 (이전 브라우저를 사용합니다.)

좋은 캐싱은 모든 컴퓨터 시스템에서 좋지만 매우 복잡합니다.자세한 내용은 https://helmetjs.github.io/docs/nocache/ #the-message를 참조하십시오.

ngbuild를 사용하여 애플리케이션을 빌드할 때는 다음 플래그를 사용해야 합니다.

--outputHashing=all

이것은 캐시 버스팅을 활성화하기 위한 것입니다.

캐시 버스팅은 고유한 파일 버전 식별자를 사용하여 브라우저에 새 파일 버전을 사용할 수 있음을 알려줌으로써 브라우저 캐싱 문제를 해결합니다.따라서 브라우저는 캐시에서 이전 파일을 검색하지 않고 오리진 서버에 새 파일을 요청합니다.

따라서 한 가지 방법은 다음을 실행하는 것입니다.

예(이전 버전)

ng build --prod --aot --output-hashing=all

다음은 전달할 수 있는 옵션입니다.--output-hashing

없음: 해시가 수행된 미디어 없음: [url|file]-loaders 번들을 통해 처리된 파일에만 해시 추가: 출력 번들에만 해시 추가: 미디어 및 번들 모두에 해시 추가 업데이트

최신 버전의 Angular(예: Angular 10)의 경우 이제 명령이 업데이트됩니다.

ng build --prod --aot --outputHashing=all

빌드 옵션 플래그에 대한 자세한 내용은 여기를 참조하십시오.https://angular.io/cli/build

빌드를 실행할 때 플래그를 추가하지 않으려면 angular.json 파일의 구성에 플래그를 설정해야 합니다.

"configurations": {
  "production": {
   "optimization": true,
    "outputHashing": "all",
     .
     .
     .
  }
}

언급URL : https://stackoverflow.com/questions/39647810/how-to-prevent-browser-cache-on-angular-2-site