I have a js module, let's call it A
. It uses versioning by appending ?v=xxxxxxxxxxxx
into its URL (like <script src="/Scripts/A.js?v=637082108844148373"></script>
). v
changes everytime we make changes in the file.
Here is the code:
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);
}
}
And then we can use it in Razor views as this:
// 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 ...
The module imports modules B.js
and C.js
:
// A.js
import {Foo} from "./B.js";
import {Bar} from "./C.js";
If I change something in module A.js
, client browser's cache is busted since we have ?v
parameter which is changing every time we make any changes in A.js
. But if I change something in module B.js
or C.js
, its version remains the same and I have to clear cache manually (CTRL F5
) to see the changes.
In other words, we can't use ?v
parameter for the lines:
import {Foo} from "./B.js";
import {Bar} from "./C.js";
How to solve this problem of cache busting for imported files in MVC 5?
CodePudding user response:
HTTP header
You could use the HTTP header to instruct the browser to no-cache
or wipe cache after a short time.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
New paths for B and C
If you like to stick to versioning, you could also put the modules B and C in a new folder (version number) and rewrite the paths in module A. Of course you don’t want to do this manually, but via a script (that ideally executes on save).
Transpile A, B and C into a single file
Easier would be to transpile the bundles into a single file, for which your versioning already works. You can use esbuild with --bundle
for that or something else - there are multiple options (also extensions that execute “on save”).
CodePudding user response:
I would work backwards from the desired final deployed state of your index.html file. My Demo SPA produces this output, but the application code does not need to deal with any cache related concerns.
<!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>
BUILD STEP
In my case I am using a Webpack build step, which keeps the application code simple. Meanwhile application modules depend on each other in a simple way.
SERVER SIDE TECH
This will be harder if you are mixing server side and client side code to manage Javascript, and personally I am not a fan of such tech stacks. The same principles apply though, so I would look into bundling options and a build step to solve your problem. The application code should not know anything about cache busting URLs.