You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Angular应用中资产缓存的最佳实践及缓存更新优化方案咨询

Angular Asset Caching Best Practices (For PDFs, Images, and Downloadables)

Great question—you're already on the right track with cache-busting, but we can refine this approach to avoid the "all-or-nothing" asset refresh and cut down on manual configuration work. Let's walk through the most effective solutions tailored to your scenario:

First off: Angular does support content-based hashing for assets—you just need to enable it in your build configuration. This is the simplest, most maintainable solution because it automates cache-busting without custom code or manual version updates.

How to Set It Up

In your angular.json, update the build options to include outputHashing:

"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": {
      // ... other settings
      "outputHashing": "media" // Adds hashes to images, PDFs, and other media assets
      // Or use "all" to hash JS/CSS files too (if needed)
    },
    "configurations": {
      "production": {
        // ... Ensure outputHashing is set here too, or inherit from base options
      }
    }
  }
}

How It Works

  • When you run ng build --prod, Angular automatically appends a content-based hash to your asset filenames (e.g., agreement.pdf becomes agreement.abc123def.pdf).
  • Your templates can reference assets normally (no custom pipes needed):
    <a [href]="'assets/agreement.pdf'">Download Agreement</a>
    
  • Angular replaces these paths in the compiled index.html (and component templates) with the hashed filenames. Since you're not caching index.html, users will always get the latest references to updated assets.
  • Keep your Nginx Cache-Control: public, max-age=31536000 rule for assets—since the filename changes when content updates, browsers will fetch the new version automatically.

Pros

  • No custom code or manual version tweaks required.
  • Only assets with changed content get new hashes (unchanged assets stay cached).
  • Fully integrated with Angular's build pipeline.

2. Automate Asset Versioning (If You Need Custom Paths)

If you prefer to keep your asset paths structured with a version directory (like your current /assets/0/ setup), you can automate the version number generation to avoid manual edits to both environment.ts and angular.json.

Example: Build-Time Script

Create a simple Node.js script (e.g., set-asset-version.js) that:

  1. Generates a version string (could be a timestamp, Git commit hash, or hash of all asset files).
  2. Updates environment.ts with the new version.
  3. Updates the asset output path in angular.json.

Here's a stripped-down example:

const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

// Generate version using Git commit hash for traceability
const assetVersion = execSync('git rev-parse --short HEAD').toString().trim();

// Update environment.ts
const envPath = path.join(__dirname, 'src/environments/environment.ts');
let envContent = fs.readFileSync(envPath, 'utf8');
envContent = envContent.replace(/assetVersion: '.*?'/, `assetVersion: '${assetVersion}'`);
fs.writeFileSync(envPath, envContent);

// Update angular.json asset output path
const angularJsonPath = path.join(__dirname, 'angular.json');
let angularJsonContent = fs.readFileSync(angularJsonPath, 'utf8');
angularJsonContent = angularJsonContent.replace(/output: "\/assets\/.*?\/"/, `output: "/assets/${assetVersion}/"`);
fs.writeFileSync(angularJsonPath, angularJsonContent);

Add this script to your package.json build workflow:

"scripts": {
  "prebuild": "node set-asset-version.js",
  "build": "ng build --prod"
}

Now when you run npm run build, the version updates automatically—no manual edits needed.

Pros

  • Maintains your preferred versioned path structure.
  • Eliminates human error from manual configuration changes.
  • Ties versioning to your Git workflow for traceability.

3. Granular Cache-Busting for Individual Assets

If you want to avoid refreshing all assets when only one changes, implement an asset manifest system. This creates a map of original filenames to hashed filenames, so only updated assets trigger a cache refresh.

How to Implement

  1. Generate an Asset Manifest: Use a build tool (like Webpack's assets-webpack-plugin or a custom script) to create a JSON file (e.g., assets-manifest.json) during build. It will look like this:
    {
      "agreement.pdf": "assets/agreement.abc123def.pdf",
      "logo.png": "assets/logo.xyz789.png"
    }
    
  2. Load the Manifest in Angular: Create a service to fetch and cache the manifest:
    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    
    @Injectable({ providedIn: 'root' })
    export class AssetService {
      private manifest: Record<string, string> = {};
    
      constructor(private http: HttpClient) {}
    
      async loadManifest(): Promise<void> {
        this.manifest = await this.http.get<Record<string, string>>('/assets-manifest.json').toPromise();
      }
    
      getAssetPath(filename: string): string {
        return this.manifest[filename] || filename;
      }
    }
    
  3. Use the Service in Templates/Components:
    <!-- In a template -->
    <a [href]="assetService.getAssetPath('agreement.pdf')">Download Agreement</a>
    
    // In a component
    constructor(private assetService: AssetService) {}
    pdfPath!: string;
    
    ngOnInit() {
      this.assetService.loadManifest().then(() => {
        this.pdfPath = this.assetService.getAssetPath('agreement.pdf');
      });
    }
    

Pros

  • Only updated assets are reloaded—unchanged assets remain cached.
  • Full control over asset paths and caching behavior.

Bonus: Optimize Nginx Configuration

Refine your Nginx config to complement your cache-busting strategy:

# Cache hashed assets indefinitely (immutable tells browsers not to revalidate)
location ~* /assets/.*\.[0-9a-f]{8,}\.(pdf|png|jpg|jpeg)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

# Never cache index.html
location = /index.html {
  expires -1;
  add_header Cache-Control "no-store, no-cache, must-revalidate";
}

# Cache non-hashed assets with a short TTL (optional fallback)
location /assets/ {
  expires 1d;
  add_header Cache-Control "public";
}

Content of the question comes from Stack Exchange, asked by Steffen Harbich

火山引擎 最新活动