【问题标题】:How to remove cache storage for every newly generated Angular production builds?如何为每个新生成的 Angular 生产版本删除缓存存储?
【发布时间】:2021-12-30 13:52:21
【问题描述】:

我有项目正在运行,我必须每天生成和部署生产版本。 但是每当我添加或升级到新版本时,我的 index.html 都不会更新,它仍然会尝试获取旧版本文件,例如runtime.xyz123.js 或 polyfills-es5.abcd1234.js" ,由于这些文件在 build 中不再存在,页面变为空白,因为浏览器无法在最新 build 中找到这些文件。

解决方法 -

  1. 如果我手动从历史记录中删除缓存,或者在隐身模式下运行项目,它可以正常工作。
  2. 如果我只进入我的本地服务器,例如http://192.168.0.164/ 它可以正常工作,但是使用我的基本 href(在创建构建时声明)例如http://192.168.0.164/angular/ 它试图获取旧的构建文件并且网页变为空白。

index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Angular Web</title>
  <meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0">
  <meta http-equiv="expires" content="0">
  <meta http-equiv="pragma" content="no-cache">
  <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <base href="/angular/">

<link rel="stylesheet" href="styles.230892a2ce1e2d0e9693.css"></head>
<body>
  <script src="assets/js/moment.js"></script>
  <script src="assets/js/moment-timezone.js"></script>
  <app-root></app-root>
<script src="runtime.71f44460add81ff0fb1e.js" defer></script><script src="polyfills-es5.950ea5f001d77cff122d.js" nomodule defer></script><script src="polyfills.05dd00906ab9ae4ef80e.js" defer></script><script src="main.5f0ba541bbf69328189e.js" defer></script></body>
</html>

Angular 版本 -

我使用下面的构建命令来生成构建 -

ng build --prod --output-hashing all --base-href /angular/

如果有什么办法可以解决这个缓存问题,请告诉我。

提前致谢!

【问题讨论】:

    标签: javascript html angular build


    【解决方案1】:

    如果您的服务器支持 .htaccess 文件,也许这可以帮助您,我使用这个 htaccess generator

    You have to enable this option 是否要阻止浏览器缓存?

    【讨论】:

    • 您好,感谢您的回复。我知道上面的链接给了我一些代码块,但是我应该把那个文件放在我的 Angular 构建环境中的哪里,请告诉我。
    • 你好,首先你需要一个 apache 服务器,并且在你放置最终 angular 包的地方你必须放置文件 .htaccess
    【解决方案2】:

    我认为有两种解决方案

    1-第一个解决方案在你的项目中添加这个脚本:

    添加/build/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]*)?(\.bundle)?.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);
      });
    

    更新文件 package.json

    "post-build": "node ./build/post-build.js"
    
    And update build command(production) : npm build && npm run build --prod && npm run post-build
    
    

    创建 version-check.service.ts

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

    在app.components.ts的ngOnInit中调用该方法

    this.versionCheckService.initVersionCheck(environment.versionCheckURL);
    

    欲了解更多信息,请查看此链接:https://blog.nodeswat.com/automagic-reload-for-clients-after-deploy-with-angular-4-8440c9fdd96c

    2- 第二次使用ServiceWorker

    你可以使用这个解决方案:

    constructor(updates: SwUpdate) {
        updates.available.subscribe(event => {
          if (promptUser(event)) {
            updates.activateUpdate().then(() => document.location.reload());
          }
        });
      }
    

    有关更多信息,请查看以下链接: https://angular.io/guide/service-worker-getting-started https://angular.io/guide/service-worker-communications

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
    • @lissettdm 谢谢你的建议
    • 嗨,感谢您的回复,我的主要问题是我的 index.html 本身正在被缓存,并显示旧的 main.js 文件。因此,无论我在 Angular 项目中添加的最新代码更改如何,它仍然会显示这些文件的旧副本。
    猜你喜欢
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 2020-08-21
    • 2016-04-29
    相关资源
    最近更新 更多