我有一个js模块,我们称它为A
。它通过将?v=xxxxxxxxxxxx
附加到其URL中来使用版本控制(类似于<script src="/Scripts/A.js?v=637082108844148373"></script>
(。每次更改文件时,v
都会发生更改。
这是代码:
public static class UrlHelperExtensions
{
public static string Content(this UrlHelper helper, string filename, bool versioned)
{
var result = filename;
if (versioned)
{
var lastWriteTimeToken = CalculateToken(helper.RequestContext.HttpContext, filename);
result = filename + "?v=" + lastWriteTimeToken;
}
return helper.Content(result);
}
}
然后我们可以在Razor视图中使用它,如下所示:
// Sample.cshtml
// ... code omitted for the sake of brevity ...
@section scripts {
<script type="module" src="@Url.Content("~/Scripts/A.js", true)"></script>
}
// ... code omitted for the sake of brevity ...
模块导入模块B.js
和C.js
:
// A.js
import {Foo} from "./B.js";
import {Bar} from "./C.js";
如果我更改模块A.js
中的某些内容,客户端浏览器的缓存将被破坏,因为我们有?v
参数,每次在A.js
中进行任何更改时,该参数都会发生变化。但是,如果我更改模块B.js
或C.js
中的某些内容,其版本将保持不变,并且我必须手动清除缓存(CTRL + F5
(才能看到更改。
换句话说,我们不能对线路使用?v
参数:
import {Foo} from "./B.js";
import {Bar} from "./C.js";
如何解决MVC 5中导入文件的缓存破坏问题?
使用ASP.NET Core,您可以使用TagHelper属性:asp-append-version
来执行缓存破坏。
<script src="/Scripts/A.js" asp-append-version="true"></script>
HTTP头
您可以使用HTTP标头来指示浏览器在短时间后使用no-cache
或擦除缓存。
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
B和C的新路径
如果你喜欢坚持版本控制,你也可以把模块B和C放在一个新的文件夹(版本号(中,并重写模块a中的路径。当然,你不想手动执行,而是通过脚本(最好在保存时执行(。
将A、B和C转换为单个文件
更容易的方法是将捆绑包转换为一个单独的文件,您的版本控制已经适用于该文件。您可以将esbuild与--bundle
一起用于此或其他用途——有多个选项(也有执行"保存时"的扩展(。
我会从index.html文件的最终部署状态向后工作。我的Demo SPA生成了这个输出,但应用程序代码不需要处理任何与缓存相关的问题。
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'>
<base href='/spa/' />
<title>OAuth Demo App</title>
<link rel='stylesheet' href='bootstrap.min.css?t=1648582395628' integrity='sha256-YvdLHPgkqJ8DVUxjjnGVlMMJtNimJ6dYkowFFvp4kKs='>
<link rel='stylesheet' href='app.css?t=1648582395628' integrity='sha256-B7pu+gcFspulW4zXfgczVtPcEuZ81tZRFYeRciEzWro='>
<body>
<div id='root' class='container'></div>
<script type='module' src='vendor.bundle.js?t=1648582395628' integrity='sha256-g0/+kYJcpXM7K5tvtIwBx//nKV3mCR8Y6NktYGHgpW0='></script>
<script type='module' src='app.bundle.js?t=1648582395628' integrity='sha256-YY15iWJ0R9wnYkc1BP9yBYMlPNCeGFJBWFhio6z8Y1Q='></script>
</body>
</html>
构建步骤
在我的案例中,我使用的是Webpack构建步骤,它使应用程序代码保持简单。同时,应用程序模块以一种简单的方式相互依赖。
服务器端技术
如果您混合使用服务器端和客户端代码来管理Javascript,这将更加困难,而且我个人不喜欢这种技术堆栈。同样的原则也适用,所以我会研究捆绑选项和构建步骤来解决您的问题。应用程序代码不应该知道任何关于缓存破坏URL的信息。
为了简单起见,如果脚本标记中有verbose,可以包含一个要检查的文件列表。
<script type="module" src="@Url.Content("~/Scripts/A.js", true, new string[]{ "~/Scripts/B.js","~/Scripts/C.js"})"></script>
然后修改您的助手以将其包含在令牌中。
public static class UrlHelperExtensions
{
public static string Content(this UrlHelper helper, string filename, bool versioned, string[] cacheCheck)
{
var result = filename;
if (versioned)
{
var cacheChecks = cacheCheck.ToList();
cacheChecks.Add(filename);
var lastWriteTimeToken = new List<string>();
foreach (var item in cacheChecks)
{
lastWriteTimeToken.Add(CalculateToken(helper.RequestContext.HttpContext, item));
}
result = filename + "?v=" + String.Join("", lastWriteTimeToken.ToArray());
}
return helper.Content(result);
}
}
未测试:(