Angular service worker and index.html caching



虽然有类似的帖子,但我找不到明确的答案是否应该使用Cache-Control标头缓存index.html

如果我错了,请纠正我,但现在我将为index.html返回Cache-Control: no-store,以避免哈希不匹配错误,从而迫使服务工作者进入降级模式。

我认为,如果具有Cache-Control: max-age=3600index.html缓存在CDN服务器上,并且应用程序将在缓存到期前更新,那么ngsw.json将返回与脚本文件不同的文件哈希,包括在index.html中,并且会发生不好的事情。正确的

此外,为了明确起见,我注意到有些人index.html添加到ngsw-config.json,这也没有意义,因为index.html是在服务工作者之前加载的。

默认情况下,包括index.html。如果你不把它包括在清单中,那么它就不会成为哈希和检查文件的一部分。如果它不在清单中(以及随后的ngsw.json),对index.html的更改将不会在服务工作者中触发事件。当然,当你下次加载/刷新网站时,它会选择新的index.html.

如果您从CDN中提供index.html,那么很可能,它是您在上次部署中构建的分发的一部分。应该正确计算。如果你在ngsw.json中有与哈希不匹配的文件,那么你上面强调的区域很重要。如果出于某种原因,你在修改index.html而没有更新整个发行版,服务人员会认为文件已损坏。它会再试一次;由于该文件与ngsw.json中的哈希不匹配,SW将认为第二次尝试已损坏并关闭。

在我的案例中,这是因为应用程序包含在构建过程中留下的令牌,这些令牌在发布管道中被Azure资源密钥替换。当应用程序构建时,散列是正确的。在这个版本中,在运行令牌替换后,我的主*.js文件与ngsw.json中的哈希值不再一致。我选择的解决方法是添加一个powershell步骤并重新计算哈希。需要注意的是,虽然实际的文件名具有唯一的哈希?嵌入的代码,您不必为服务工作者的工作进行更正。filename/hash-key/value对必须指向一个有效的文件,并且该文件的SHA1哈希必须与ngsw.json中的哈希相匹配。我为对哈希进行编译后验证/更正而编写的脚本如下。如果您有独立于整个发行版更新index.html的进程,请使用此脚本更新ngsw.json,并将其包含在index.html推送中。

注意:

  • 脚本接受3个参数。如果未通过,则假设:
    • 脚本是从angular项目的根运行的
    • 工作目录是"./dist"(要检查的脚本所在的位置)
    • 输入路径为"<working_dir>/ngsw.json"
    • 输出路径为"<working_dir>/ngsw_out.json"
  • 如果要修改文件,请确保指定相同的输入路径和输出路径
  • 如果你把它放在AzDO中,你需要选中"使用Powershell Core"复选框

Powershell脚本开始:

param([string]$working_path = "./dist"
, [string]$input_file_path = "$working_path/ngsw.json"
, [string]$output_file_path = "$working_path/ngsw_out.json")
"Checking for existence of hash script..."
$fileExists = Test-Path -Path $input_file_path
if ($fileExists) {
"Service Worker present.  Beginning hash reconciliation."
""
$files_to_calc = @()
$ngsw_json = (Get-Content $input_file_path -Raw) | ConvertFrom-Json
"-----------------------------------------"
"Getting list of javascript files to check"
"-----------------------------------------"
$found_count = 0
for ($idx = 0; $idx -lt $ngsw_json.hashtable.psobject.properties.name.count; $idx++) {
$current_file = $ngsw_json.hashtable.psobject.properties.name[$idx]
if ($current_file.Contains(".js")) {
$files_to_calc += $current_file
"   File {$idx} $($files_to_calc[-1]) found."
$found_count++
}
}
"---------------------------------------"
"$($files_to_calc.count) files to check."
"---------------------------------------"
$replaced_count = 0
$files_to_calc | ForEach-Object {
$new_hash_value = (Get-FileHash -Algorithm SHA1 "$($working_path)$_").Hash.ToLower()
$current_hash_value = $ngsw_json.hashTable.$_
$current_index = [array]::IndexOf($ngsw_json.hashTable.psobject.properties.name, $_)
$replaced = $false
if ($ngsw_json.hashTable.$_ -ne $new_hash_value) {
$ngsw_json.hashTable.$_ = "$new_hash_value"
$replaced = $true
$replaced_count++
}
"$($replaced ? '** ' : '   '){$current_index}:$_ --- Current Value: " +
"$($current_hash_value.substring(0, 8))... New Value: " +
"$($new_hash_value.substring(0, 8))..."
}
""
"--> Replaced $replaced_count hash values"
$ngsw_json | ConvertTo-Json -depth 32 | set-content "$output_file_path"
}
else {
"Service Worker missing.  Skipping."
}

我不是这方面的专家,但我确信以下链接将帮助您消除疑虑。

https://angular.io/guide/service-worker-getting-started#whats-正在缓存

缓存的是什么

请注意,浏览器渲染此应用程序所需的所有文件都已缓存。ngsw-config.json样板文件配置用于缓存CLI:使用的特定资源

  • index.html.

  • favicon.ico.

  • 构建工件(JS和CSS捆绑包)。

  • 资产下的任何东西。

  • 直接位于配置的outputPath(默认情况下为.dist//)或resourcesOutputPath下的图像和字体。查看ng构建有关这些选项的详细信息。

下面的链接包含有关Service worker和应用程序资源缓存的信息。我想让你从中了解应用程序版本、更新检查和资源完整性。

https://angular.io/guide/service-worker-devops#service-工作程序和缓存应用程序资源

我也将这三个部分的内容粘贴在这里,只是为了避免这个答案成为"仅链接的答案">

应用程序版本

在Angular服务工作者的上下文中,"版本"是表示Angular应用程序的特定构建的资源集合。每当部署应用程序的新版本时,服务工作者都会将该版本视为应用程序的一个新版本。即使只更新了一个文件,情况也是如此。在任何给定的时间,服务工作者的缓存中都可能有多个版本的应用程序,并且可能同时为它们提供服务。有关更多信息,请参阅下面的应用程序选项卡部分。

为了保持应用程序的完整性,Angular服务工作者将所有文件分组到一个版本中。分组为一个版本的文件通常包括HTML、JS和CSS文件。这些文件的分组对于完整性至关重要,因为HTML、JS和CSS文件经常相互引用并依赖于特定的内容。例如,一个index.html文件可能有一个引用bundle.js的标记,它可能试图从该脚本中调用函数startApp()。每当提供这个版本的index.html时,都必须提供相应的bundle.js。例如,假设两个文件中的startApp()函数都重命名为runApp()。在这种情况下,为旧的index.html(它调用startApp())和新的bundle(它定义runApp()。

这种文件完整性在延迟加载模块时尤为重要。JS捆绑包可能引用许多惰性块,并且惰性块的文件名对于应用程序的特定构建是唯一的。如果运行版本为X的应用程序尝试加载延迟区块,但服务器已更新到版本X+1,则延迟加载操作将失败。

应用程序的版本标识符由所有资源的内容决定,如果其中任何资源发生变化,它就会发生变化。在实践中,版本由ngsw.json文件的内容决定,该文件包括所有已知内容的散列。如果任何缓存的文件发生更改,文件的哈希将在ngsw.json中发生更改,导致Angular服务工作者将活动的文件集视为新版本。

通过Angular服务工作者的版本控制行为,应用程序服务器可以确保Angular应用程序始终具有一组一致的文件。

更新检查

每次用户打开或刷新应用程序时,Angular服务工作者都会通过查找ngsw.json清单的更新来检查应用程序的更新。如果找到更新,则会自动下载并缓存,并在下次加载应用程序时提供。

资源完整性

长缓存的潜在副作用之一是无意中缓存了无效资源。在正常的HTTP缓存中,硬刷新或缓存过期限制了缓存无效文件的负面影响。服务工作者会忽略这些限制,并有效地长缓存整个应用程序。因此,服务工作者获得正确的内容是至关重要的。

为了确保资源的完整性,Angular服务工作者验证其拥有哈希的所有资源的哈希。通常,对于使用Angular CLI创建的应用程序,这是用户src/ngsw-config.json配置所覆盖的dist目录中的所有内容。

如果某个特定文件未通过验证,Angular服务工作人员会尝试使用"缓存破坏"URL参数重新获取内容,以消除浏览器或中间缓存的影响。如果该内容也未通过验证,则服务工作者会认为该应用程序的整个版本无效,并停止为该应用程序提供服务。如有必要,服务工作者会进入一种安全模式,在这种模式下,请求会回到网络上,如果提供无效、损坏或过时内容的风险很高,则选择不使用其缓存。

哈希不匹配可能由于多种原因而发生:

  • 原始服务器和最终用户之间的缓存层可以提供陈旧的内容
  • 非原子部署可能导致具有部分更新可见性的角度服务工作者内容
  • 生成过程中的错误可能导致更新资源,而不更新ngsw.json。反之亦然发生,导致更新的ngsw.json没有更新的资源

我认为您有必要了解Angular应用程序工作流程和Angular Service Worker运行时缓存机制。所以我要在这里写下关于他们的事情。这将有助于你的理解。

Angulars按照以下步骤开始工作

  • Angular以main.ts开头
  • Angular应用程序是引导的,app.module.ts作为参数传递
  • Angular分析应用程序组件,读取那里传递的设置,然后有SELECTOR应用程序根
  • 现在,Angular可以处理index.html中的应用程序根,并且知道SELECTOR的规则
  • SELECTOR应该插入应用程序组件,并附上一些HTML代码-一个模板-HTML组件

Angular ServiceWorker

Angular CLI还包含在Angular应用程序根模块中Service Worker模块。CLI还添加了一个名为ngsw-config.json的新配置文件,用于配置AngularServiceWorker运行时行为,生成的文件附带一些智能默认值。这里发生了很多事情,所以让我们逐步分解。此文件包含默认缓存行为或Angular Service Worker,其目标是应用程序静态资产文件:index.htmlCSSJavascript捆绑包。

Angular Service Worker可以缓存浏览器缓存存储。这是一个基于Javascript的键/值缓存与标准浏览器无关的机制缓存控制机制,这两种机制可以单独使用。

应用程序部分下的文件是应用程序:单个页面由其index.html加上其CSSJs束。应用程序的每个页面都需要这些文件,并且不能延迟加载

在这些文件的情况下,我们希望尽早缓存它们这就是应用程序缓存配置确实如此。应用程序文件将是主动的Service Worker在后台下载并安装,这就是安装模式预取的含义。服务worker不会等待相反,它将提前下载它们并且缓存它们,以便下次请求它们时可以为它们提供服务。对于那些共同构成应用程序本身(index.htmlCSSJavascript捆绑包),因为我们已经知道我们将一直需要它们。


index.html依赖于index.js,后者依赖于chunk.js块是从浏览器缓存加载的。

最新更新