在Angular 6 Service Worker中通过新鲜度缓存资产

Rei*_*uko 15 caching offline-caching service-worker angular

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

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

下面的示例是一个标准的Angular App,它不包含我正在使用的实际项目,但显示了我想要缓存的元素,结构如下所示:

\_ 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
     |_ ...
Run Code Online (Sandbox Code Playgroud)

这里有相关配置:

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"]
            }
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

是否有可能freshness通过使用installModeupdateMode资产来实现策略等缓存行为?

我试图将它从资产缓存中排除,它是通过网络加载的,但显然不会由服务工作者在脱机后提供.

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

"dataGroups": [
    {
        "name": "config",
        "urls": ["assets/excluded_asset.js"],
        "cacheConfig": {
            "maxSize": 10,
            "maxAge": "1d",
            "timeout": "100",
            "strategy": "freshness"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我错过了什么或者没有办法通过freshness策略缓存资产?最好不要移动文件或更改文件的请求方式.

编辑

我试图将它移到缓存的资产目录之外,并将其包含在dataGroups设置中,也不起作用.

小智 6

我也遇到过同样的问题。我发现始终拥有最新的 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/**"]
      }
    }
  ]
}

Run Code Online (Sandbox Code Playgroud)

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

当然,这以一种非常明显的方式很糟糕:你的index.html不会被服务工作者缓存,但至少它允许返回的用户直接拥有最新的文件。

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


Kou*_*sic 5

服务人员的新应用

该命令将如下所示:

ng new myApp --service-worker (or using the alias?—?-sw )
Run Code Online (Sandbox Code Playgroud)

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

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

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

将Angular Service Worker添加到现有应用程序

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

1.安装NGSW

npm install @angular/service-worker --save
Run Code Online (Sandbox Code Playgroud)

2.启用构建支持(仅适用于Angular CLI 1.6,请参见以下通知)

ng set apps.0.serviceWorker=true
Run Code Online (Sandbox Code Playgroud)

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

重要!目前,当我们使用Angular CLI 1.5时,请确保您在中没有此属性.angular-cli.json,这将导致生成错误。请参阅下面的Angular CLI 1.5中的模拟步骤。

3.注册NGSWAppModule。这是在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 { }
Run Code Online (Sandbox Code Playgroud)

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/**"
      ]
    }
  }]
}
Run Code Online (Sandbox Code Playgroud)

目前,在使用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"
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们运行,npm run build-prod-ngsw我们将在dist文件夹中包含Angular PWA 。(可选)我们可以http-server通过运行使用最简单的服务npm run serve-prod-ngsw

重要!不要ng serve用于测试您的Angular Service Worker。该开发服务器未设计为与PWA flow协同工作。始终构建该应用程序的生产版本,并使用任何静态Web服务器从您的分发文件夹中提供该应用程序。

应用程序外壳

如果我们执行上述操作并运行npm run build-prod-ngswAngular PWA,则其默认格式已为我们准备好了!部署该应用程序,或仅使用任何静态Web服务器在本地运行该应用程序(http-server在我的情况下为包,您可以运行npm run serve-prod-ngsw以生成并提供服务)。

我们脱机后该应用程序将正常工作。为什么?由于NGSW缓存了配置文件的assetGroups部分中列出的所有资源,现在它负责从Cache Storage(现在已充满记录)中为它们提供服务:

在此处输入图片说明

服务人员已注册并处于活动状态

在此处输入图片说明

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

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

在此处输入图片说明

NGSW的存储类型

  • 带postfix的条目:cache-实际的HTTP响应。
  • 带后缀:meta?的条目用于存储版本控制元信息。以后,可能会将这种存储的数据移至indexedDB

如果将DevTools保持打开状态,则在“服务工作者”侧执行每个操作之后,“高速缓存存储”部分中的条目很可能不会自动更新。如果您希望查看实际数据,请右键单击并选择“刷新缓存”。

对。NGSW配置文件的默认格式不足以满足我们的要求,因为我们使用的是Material Icons webfont。显然,这些资源(对应的CSS和WOFF2文件)不是由NGSW缓存的,但是我们可以通过assetGroups在default app和group 之外再添加一个group来轻松修复它assets。让我们称之为fonts

{
  ...
  "assetGroups": [
   ...
   {
    "name": "fonts",
    "resources": {
      "urls": [
        "https://fonts.googleapis.com/**",
        "https://fonts.gstatic.com/**"
      ]
    }
  }]
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

在此处输入图片说明

NGSW生成的存储

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

在此处输入图片说明

assetGroups和之间存在根本区别dataGroups

  • assetGroups 正在跟踪应用[shell]版本。
  • dataGroups与应用程序版本无关。它们使用自己的缓存策略进行缓存,这是处理我们的API响应的适当部分。

运行时缓存

对我的/timelineAPI端点使用网络优先策略,对端点使用Cache-First策略/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"
      }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

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

现在构建,投放,单击“加载我的时间轴”和“加载我的收藏夹”按钮以获取并缓存API响应,然后切换到离线状态。

关于在线模式的优化。返回在线并单击Timeline / Favorites一次或两次。很明显,收藏夹会立即加载,这是因为我们跳过了整个网络行程,并从缓存中获取了数据。使用cacheConfig部分中的设置指定要缓存多长时间?我们在此具有细粒度控制。

NGSW通过一些真正智能的网络优化为我们提供了很多帮助,这些优化仅需要我们提供一些JSON配置。