静的HTMLからScriptとCSSファイルを動的URLで読込む方法
October 05, 2024
Webアプリの開発で、静的なHTMLと共に外部呼出されるスクリプトやCSSの内容の変更してファイルを更新しても、 ブラウザにキャッシュされた古いスクリプト・CSSファイルが使用されてしまい、正常に画面表示されないケースがあると思います。 そこで、静的HTMLであっても、スクリプト・CSSを動的なURLで読込むことでキャッシュ使用を防ぐ方法をご紹介します。
スクリプト・CSSの外部リソースを読込むためのスクリプトを用意することで対応することが可能です。 こちらをクリックすると別ウィンドウで実際のサンプル画面が表示されます。
同じコンテンツに対して、初回(青枠)は、サーバーから読込まれて、2回目(緑枠)は、ブラウザキャッシュから読込まれています。 一方、URLのバージョンの値を変更した3回目(赤枠)は、サーバーから読込まれていることがわかると思います。
HTML側での記述
スクリプト・CSSを動的に読込むためのスクリプトを記述したファイル「loader.js」を読込むため、 HTML側にDOMで「script」要素を作成しhead要素に追加して読込みを行います。
「version」変数は、静的ファイルのURLにバージョンを付与するこを目的としています。 コンテンツの更新と合わせてその値を変更することで、URLも変わることになります。 ブラウザは、一般的にパラメータを含むURL単位でコンテンツをキャッシュしているため、上記方法でキャッシュの古いファイルの使用を防止することができます。 例えば、ランダムな値を付与する方法もありますが、CDNなどのコンテンツキャッシュサーバーを使用している場合は、その効果が無くなってしまうため、バージョン管理した固定値がよいと思います。
「srcList」変数は、読込みたいスクリプト・CSSファイルの各URLを配列で記載しています。なお、「loadSource」関数は、「loader.js」に記述されています。
また、CSSが読込まれる前に、HTMLの内容が一瞬見えてしまうため、「content」要素に「opacity:0;」を記載して初期値を透明にしています。 そのうえで、全てのファイルが読込み完了した後に内容が表示されるように「opacity:1;」に変更しています。
<html>
<head>
...
<script type="text/javascript">
const version = '1.0';
const srcList = [
"./js/script.js",
"./css/style.css"
];
(function(){
const onLoad = function(){
loadSource(srcList, version);
};
const appendScript = document.createElement('script');
appendScript.async = false;
appendScript.src = "./js/loader.js?_ver=" + version;
appendScript.onload = onLoad;
document.head.appendChild(appendScript);
})();
...
</script>
</head>
<body>
<div class="content" style="opacity:0;">
...
</div>
...
</body>
</html>
Script側での記述
「loader.js」に記述している「loadSource」関数は以下になります。
「srcList」に記載のスクリプト・CSSファイルを順番に且つ同期で読込みたいため、現在のファイルの読込み完了後に次のファイルの読込みが開始する必要があります。 そのため、読込み用に追加した要素の「onload」イベントで「loadSource」関数を再帰的に呼出しています。
const loadSource = function(srcList, version, onLoad, onError){
if(!srcList) return;
const lastIndex = srcList.lastIndex !== undefined ? srcList.lastIndex : 0;
let src = srcList[lastIndex];
srcList.lastIndex = lastIndex + 1;
if(!src){
const finished = function(){
const content = document.getElementsByClassName("content");
if(!content){
setTimeout(finished, 10);
return;
}
content[0].style.opacity = 1;
};
setTimeout(finished, 0);
return;
}
let appendElement = null;
let onLoadCallback = onLoad;
let onErrorCallback = onError;
if(src.toLowerCase().endsWith(".js")){
appendElement = document.createElement("script");
appendElement.async = false;
appendElement.src = src + (version ? (src.indexOf("?") === -1 ? "?" : "&") + "_ver=" + version : '');
if(!onLoadCallback) onLoadCallback = function(element){ console.log("script onload " + element.src); };
if(!onErrorCallback) onErrorCallback = function(element){ console.log("script onError " + element.src); };
}
else if(src.toLowerCase().endsWith(".css")){
appendElement = document.createElement("link");
appendElement.rel = "stylesheet";
appendElement.href = src + (version ? (src.indexOf("?") === -1 ? "?" : "&") + "_ver=" + version : '');
if(!onLoadCallback) onLoadCallback = function(element){ console.log("style onload " + element.href); };
if(!onErrorCallback) onErrorCallback = function(element){ console.log("style onError " + element.href); };
}
else{
console.log("unknown suffix " + src);
return;
}
appendElement.onload = function() {
onLoadCallback(this);
loadSource(srcList, version, onLoad, onError);
};
appendElement.onerror = function() {
onErrorCallback(this);
loadSource(srcList, version, onLoad, onError);
};
document.head.appendChild(appendElement);
};
最後までお読みいただきありがとうございました!ご意見などございましたらメッセージをお寄せいただけると幸いです。
Web · Script · CSS


