【问题标题】:Cache asset via freshness in Angular 6 Service Worker通过 Angular 6 Service Worker 中的新鲜度缓存资产
【发布时间】:2023-04-07 13:27:01
【问题描述】:

我正在尝试将 Angular Service Worker 集成到现有项目中。如果我理解正确的话,有两种情况是如何在 Angular SW 中缓存数据的。可以预取或延迟更新资产数据并缓存特定的 API 调用和其他 XHR 请求。

我想要实现的是首先通过网络加载特定资产,如果请求超时或无法访问,它将通过缓存提供服务。就像缓存 API 调用时的 freshness 策略一样。 但似乎没有办法为 Angular 项目中作为资产加载的 JS 文件配置这种新鲜度加载机制。 我已经设置了一个示例项目进行测试:https://github.com/philipp-schaerer-lambdait/angular-service-worker-test

以下示例是标准 Angular 应用程序,不包含我正在使用的实际项目,但显示了我要缓存的元素,结构如下所示:

\_ Angular root  
 |_ src/
   |_ index.html <----------- links to excluded_asset.js
   |_ app/
   |_ assets/
     |_ excluded_asset.js <-- this one is excluded in ngsw-config.json
     |_ included_asset.js
     |_ ...

这里是相关配置:

ngsw-config.json

{
    "index": "/index.html",
    "assetGroups": [
        {
            "name": "app",
            "installMode": "prefetch",
            "resources": {
                "files": ["/favicon.ico", "/index.html", "/*.css", "/*.js"]
            }
        },
        {
            "name": "assets",
            "installMode": "lazy",
            "updateMode": "prefetch",
            "resources": {
                "files": ["/assets/**", "!/assets/excluded_asset.js"]
            }
        }
    ]
}

是否可以通过对资产使用installModeupdateMode 来实现类似freshness 策略的缓存行为?

我尝试将它从资产缓存中排除,它是通过网络加载的,但显然在离线后不会由 service worker 交付。

之后,我尝试通过dataGroups 再次包含它并将策略设置为freshness,但似乎一旦从资产配置中排除资产,它就不会再次被缓存。另外我不认为dataGroups 设置可以用于这个文件。

"dataGroups": [
    {
        "name": "config",
        "urls": ["assets/excluded_asset.js"],
        "cacheConfig": {
            "maxSize": 10,
            "maxAge": "1d",
            "timeout": "100",
            "strategy": "freshness"
        }
    }
}

我是否遗漏了什么或者无法通过freshness 策略缓存资产?最好不要移动文件或更改文件的请求方式。

编辑

我尝试将其移出缓存资产目录并将其包含在 dataGroups 设置中,但也没有用。

【问题讨论】:

    标签: angular caching service-worker offline-caching


    【解决方案1】:

    使用 Service Worker 的新应用

    命令如下:

    ng new myApp --service-worker (or using the alias — -sw )
    

    有了这个服务工作者标志,Angular CLI 1.6 将为我们做一些自动化:

    1. Angular Service Worker 包将被安装。
    2. 将启用对 NGSW 的构建支持。
    3. 将为您的申请注册 NGSW。
    4. 将使用一些智能默认值创建 NGSW 配置文件。

    不管怎样,即使在 CLI 1.6 发布之后,知道如何重现这些步骤也很好,因为我们必须手动执行它们才能将 NGSW 支持添加到现有应用程序。让我们在 PWAtter 中添加 Angular Service Worker。

    向现有应用添加 Angular Service Worker

    让我们手动执行上面相同的 4 个步骤:

    1.安装 NGSW

    npm install @angular/service-worker --save
    

    2。启用构建支持(仅适用于 Angular CLI 1.6,请参阅下面的通知)

    ng set apps.0.serviceWorker=true
    

    或在.angular-cli.json 文件中手动添加/编辑此参数。

    重要!目前,当我们使用 Angular CLI 1.5 时,请 确保您在.angular-cli.json 中没有此属性,它会 导致构建错误。了解如何在 Angular CLI 1.5 中模拟此步骤 下面。

    3.在您的AppModule 中注册 NGSW。这就是它在 Angular 中的样子 CLI 1.6:

    import { ServiceWorkerModule } from '@angular/service-worker'
    import { environment } from '../environments/environment';
    
    ...
    
    @NgModule({
      imports: [
        ...
        environment.production ? ServiceWorkerModule.register('/ngsw-worker.js') : []
      ],
      ...
    })
    export class AppModule { }
    

    4.创建 NGSW 配置文件(默认名称为 src/ngsw-config.json)。这是 Angular CLI 1.6 将生成的默认内容。

    {
      "index": "/index.html",
      "assetGroups": [{
        "name": "app",
        "installMode": "prefetch",
        "resources": {
          "files": [
            "/favicon.ico",
            "/index.html"
          ],
          "versionedFiles": [
            "/*.bundle.css",
            "/*.bundle.js",
            "/*.chunk.js"
          ]
        }
      }, {
        "name": "assets",
        "installMode": "lazy",
        "updateMode": "prefetch",
        "resources": {
          "files": [
            "/assets/**"
          ]
        }
      }]
    }
    

    目前,在使用 Angular CLI 1.5 时,我们还必须模拟第 2 步的构建支持。实际上,除了 ng build --prod 命令之外,还需要执行 2 个额外的操作(使用生产构建很重要,以便使用 NGSW!):

    使用 NGSW CLI ngsw-config 根据 NGSW 配置文件 src/ngsw-config.json 生成 NGSW 控制(清单)文件 ngsw.json。

    将 NGSW 本身从 npm_modules 包文件夹复制到我们的 dist 文件夹。

    为了有一个简单的命令来生成支持 NGSW 的生产版本,让我们添加一些 npm 脚本:

    {
      ...
      "scripts": {
        ...
        "ngsw-config": "node_modules/.bin/ngsw-config dist src/ngsw-config.json",
        "ngsw-copy": "cp node_modules/@angular/service-worker/ngsw-worker.js dist/",
        "build-prod-ngsw": "ng build --prod && npm run ngsw-config && npm run ngsw-copy",
        "serve-prod-ngsw": "npm run build-prod-ngsw && http-server dist -p 8080"
      }
    }
    

    现在,如果我们运行 npm run build-prod-ngsw,我们将在 dist 文件夹中拥有 Angular PWA。或者,我们可以使用最简单的 http-server 通过运行 npm run serve-prod-ngsw 来提供它。

    重要!不要使用ng serve 来测试你的Angular Service Worker。 此开发服务器并非旨在与 PWA 流。始终构建应用程序的生产版本并提供服务 使用任何静态 Web 服务器从您的分发文件夹中。

    应用程序外壳

    如果我们执行上述操作并运行npm run build-prod-ngsw — 默认形式的 Angular PWA 已经为我们准备好了!部署应用程序或使用任何静态 Web 服务器在本地运行它(在我的情况下,http-server 包,您运行 npm run serve-prod-ngsw 来构建和服务)。

    我们下线后应用程序将继续运行。为什么?因为 NGSW 缓存了配置文件的assetGroups部分列出的所有资源,现在它负责从缓存存储中提供它们,缓存存储现在已经满是记录了:

    Service Worker 已注册并处于活动状态

    我们可以查看缓存响应的内容(目前仅在Chrome Canary中可用)

    NGSW 使用缓存存储来存储 HTTP 响应数据和一些元数据来处理版本控制:

    NGSW 的存储类型

    • 后缀为 :cache  的条目— 实际的 HTTP 响应。
    • 带有后缀:meta 的条目— 用于存储版本控制元信息。稍后这种存储的数据可能会移动到indexedDB

    如果您保持 DevTools 打开,缓存存储部分中的条目 很可能不会在每次操作后自动更新 服务人员方面。如果您想查看实际数据,请右键单击 并选择刷新缓存。

    没错。 NGSW 配置文件的默认形式对于我们的案例来说是不够的,因为我们使用 Material Icons webfont。显然,这些资源(对应的 CSS 和 WOFF2 文件)没有被 NGSW 缓存,但是我们可以通过在默认的 appassets 之外再添加一组到 assetGroups 来轻松修复它。我们就叫它fonts

    {
      ...
      "assetGroups": [
       ...
       {
        "name": "fonts",
        "resources": {
          "urls": [
            "https://fonts.googleapis.com/**",
            "https://fonts.gstatic.com/**"
          ]
        }
      }]
    }
    

    使用 glob 语法指定这些资源是有意义的,因为字体文件的确切 URL 可能会不时更改以支持 webfont 版本控制。此外,您可能会注意到我们既没有指定installMode 也没有指定updateMode。一方面,两者都将在生成的 NGSW 控制文件中设置为 prefetch,因为这是默认值。另一方面,它们只有在被请求后才会被缓存,因为urls 的具体规定是列出资源的方式。

    在我们重建、运行并切换到离线模式后,我们将看到应用程序的正常状态以及所有图标。

    在缓存存储中,我们将看到两个新条目:

    NGSW 生成的存储

    我们甚至可以预览缓存的字体:

    assetGroupsdataGroups 之间存在根本区别。

    • assetGroups 正在跟踪应用 [shell] 版本。
    • dataGroups 独立于应用程序版本。它们被缓存使用 他们自己的缓存策略,这是处理我们的适当部分 API 响应。

    运行时缓存

    对我的/timeline API 端点使用网络优先策略,对/favorites 端点使用缓存优先策略。 src/ngsw-config.json 中的相应设置如下所示:

    {
      ...
      "dataGroups": [{
          "name": "api-freshness",
          "urls": [
            "/timeline"
          ],
          "cacheConfig": {
            "strategy": "freshness",
            "maxSize": 100,
            "maxAge": "3d",
            "timeout": "10s"
          }
        },
        {
          "name": "api-performance",
          "urls": [
            "/favorites"
          ],
          "cacheConfig": {
            "strategy": "performance",
            "maxSize": 100,
            "maxAge": "3d"
          }
        }
      ]
    }
    

    有一个定义 NGSW 行为的主开关:cacheConfig / strategy。对于网络优先策略,它是freshness,对于缓存优先 — performance

    现在构建、提供服务,单击加载我的时间线和加载我的收藏夹按钮以获取和缓存 API 响应,然后切换到离线状态。

    关于在线模式的优化。返回在线并单击Timeline / Favorites 一次或两次。可以清楚地看到收藏夹立即加载,只是因为我们跳过了整个网络行程并从缓存中获取数据。要指定缓存多长时间使用cacheConfig 部分中的设置 — 我们在那里有细粒度的控制。

    NGSW 在一些非常智能的网络优化方面为我们提供了很多帮助,只需要我们进行一些 JSON 配置。

    【讨论】:

    • 感谢您为 Angular Service Worker 提供详细的分步安装说明。它没有直接回答我关于如何在可能的情况下首先通过网络获取资产以及在返回超时或网络错误时如何通过缓存获取资产的问题。我会尝试一些 maxAge 和 maxSize 配置设置并再次提供反馈。
    【解决方案2】:

    我也遇到了同样的问题。我发现始终拥有最新的 js 和 css 文件的解决方案只是从缓存资产中排除 index.html。

    {
      "index": "/index.html",
      "assetGroups": [
        {
          "name": "app",
          "installMode": "prefetch",
          "updateMode": "prefetch",
          "resources": {
            "files": [
              "/*.css",
              "/*.js",
              "!/index.html" // Right here!!!!!
            ]
          }
        },
        {
          "name": "assets",
          "installMode": "lazy",
          "updateMode": "lazy",
          "resources": {
            "files": ["/static/**"]
          }
        }
      ]
    }
    
    

    确保在您的 Angular 构建配置中使用 "outputHashing": "all",。这样,当您更改代码时,它将生成一个具有不同名称的文件。然后它会自动将脚本标签(或链接标签)添加到您的 html 文件(服务工作者将忽略该文件),并且一旦您将更改推送到生产环境,index.html 将指向您的新 js 和 css 文件。

    当然,这很糟糕:你的 index.html 不会被 service worker 缓存,但至少它可以让返回的用户直接获得最新的文件。

    我真的希望有一种方法也可以为资产提供“新鲜度”选项...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-10
      • 2019-10-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多