Right now I'm developing widget-like application using React+Webpack and injecting bundled CSS/JS files dynamically via script (see code) on landing page project (it's pretty old huge legacy project and doesn't know anything about mentioned technologies) so that's why this kind of old fashioned script is used (without usage of dynamic imports etc.).
function load(onSuccess) {
const styles = document.createElement("link");
styles.rel = "stylesheet";
styles.href = "/particular-widget/widget.main.css";
document.head.appendChild(styles);
const script = document.createElement("script");
script.onload = onSuccess;
script.src = "/particular-widget/widget.main.js";
document.head.appendChild(script);
}
Everything was good until it was required to update some functionality of this widget React app. We added it and redeployed widget BUT end users started getting old version of widget because of browser caching (file names are the same).
We started looking for solutions:
TRY 1
First thing which came to our mind was webpack content hashes functionality (every new build - new content hashes). Files started looking like this:
- /particular-widget/widget.main.[contenthash].css
- /particular-widget/widget.main.[contenthash].js
But after that script above become broken because it's not possible to use, let's say, regular expressions in href/src attributes.
TRY 2
Adding timestamps as query parameters to files (see code below). This is also not a good solution because we still need caching of specific widget versions.
function load(onSuccess) {
const styles = document.createElement("link");
styles.rel = "stylesheet";
styles.href = "particular-widget/widget.main.css" + '?t=' + Date.getTime();
document.head.appendChild(styles);
const script = document.createElement("script");
script.onload = onSuccess;
script.src = "particular-widget/widget.main.js" + '?t=' + Date.getTime();
document.head.appendChild(script);
}
POSSIBLE SOLUTION
We thought about API solution which calculates hashes of files on backend and returns them to client so they can be used as query params.
function load(onSuccess) {
getFileHashesAPI(['particular-widget/widget.main.css', 'particular-widget/widget.main.js'],
function(hashes, err) {
if (err) {
//error handling
}
const styles = document.createElement("link");
styles.rel = "stylesheet";
styles.href = "particular-widget/widget.main.css" + '?h=' + hashes[0];
document.head.appendChild(styles);
const script = document.createElement("script");
script.onload = onSuccess;
script.src = "particular-widget/widget.main.js" + '?h=' + hashes[1];
document.head.appendChild(script);
})
}
This solution should work fine but it requires implementing new API endpoint in really old java code base and we still hope that there's pure frontend solution=).
Nice to meet:
- Because of different release strategies for landing page and widget itself we need to have kind of generic script on landing page which should NOT be changed on every new build of widget
- Browser caching should work for every new version of widget
- Changes should be made in widget code or landing page JS script
So, folks, I was wondering if you could help me to resolve this matter.
Many thanks in advance!