何かと便利な、パブリックCDN
CDNはコンテンツ・デリバリー・ネットワークの略で、配信を高速化することに特化したネットワークのことをいいます。
むろん、アクセスの多いサイトを運営している場合、商用のCDNを契約することがあるかもしれませんが、そうでなくても便利な場面があります。Google(Google Hosted Libraries)やMicrosoft(Microsoft Ajax Content Delivery Network)、MaxCDN(BootstrapCDN)などのCDN提供側の事業者が、オープンソースプロジェクトをホスティングさせていて、誰でも自由に利用可能となっているのです。これらを使うことで、
- CDN経由で高速な配信を、そのライブラリについて受けられる
- テスト程度に書き進める場合にも、ライブラリのファイルを用意せずに始められる
- 同じファイルを他のサイトでも使っていた場合、キャッシュを共用できる
など、各種のメリットがあります。ただ、一方で「万が一CDNが落ちた場合に、必要なライブラリが揃わなくなる」ということが起きます。
とはいえ、うまく工夫すれば、「ふだんはCDN経由で取得しておいて、読み損なった場合にのみローカルサーバのJS/CSSファイルへフォールバックする」ということが行なえます。
JavaScriptの場合
JavaScriptの中にも、jQuery本体のように、直後のJavaScriptロードなどDOMContentLoaded
前に必要なために「その場で読み込むのが適当なもの」と、DOMContentLoaded
時点で揃っていればそれでいいものがありますので、その2つに分けて考えます。
読み込み時の動作
ブラウザで<script src>
を読み出した場合、ロードと実行のタイミングは以下のようになります。
-
async
やdefer
なしの場合…その場でロードして実行1 -
defer
あり…DOMContentLoaded
の前に、(defer
どうしは順番に従って2)実行 -
async
あり…onload
の前のどこかでロードして実行
ということで、これらに従って「正しく動作すれば読み込みが行われるはずのタイミング」でフォールバック処理を入れることとなります。
その場でロードが必要なもの
async
やdefer
なしの場合、次の行に<script>
要素を置けば、その中ではロードが終わっているはずです。そして、ブラウザで使うJavaScriptにはたいていの場合、グローバルに現れるオブジェクトがあります(jQueryプラグインの場合は$.fn.xxx
に現れる)ので、それの有無を検知して、なければdocument.write
で<script>
を書き出してしまいましょう(document.write
を入れるとDOMが作り直しになりますが、こういう場合はむしろ適切です)。
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/path/to/local/jquery.js"><\/script>')</script>
ここで1点注意ですが、<script>
要素内のインラインスクリプトに</script>
と書くと、そこでスクリプトが途切れてしまいます。回避するには、
-
<\/script>
のようにエスケープする -
unescape(''%3C/script%3E'')
のようにエスケープから戻す -
'<' + '/script>'
のように分ける
など、いくつか方法があります。
async
で読み込むもの
async
をかけた場合、ロードのタイミングが「onload
前のどこか」ということになります。ということで、window.onload
にイベントを仕掛けて、読み込まれていなければ<script>
要素をDOMで生成、という流れでいいでしょう。
CSSの場合
CSSの場合、JavaScript以上に「正しく読み込まれているかをFeature Detectionで確認する」というのが面倒になります。もっと楽な方法として、「<link>
要素にonload
を仕掛けて、そこから処理」という方法が考えられます。なお、この方法に対応するのは、
- Firefox 9以上
- Chrome 19以上
- Edge
- IE 6以上
- Safari 6以上(iOSも含む)
- Opera 9以上(Blink版も含む)
となっていて、「Android標準ブラウザ」では動きませんので、注意しましょう(情報元)。
また、CSSのロードはonload
以前に行われれるもので、DOMContentLoaded
はCSSのロードを待ちませんので、onload
に仕掛ける必要があります。一方で、CSSどうしに(こっちをロードしておかないと別のがロードにも失敗する、という意味合いでの)依存関係は発生し得ないので、後から差し替えても問題なく表示できます。
簡潔に書けるようにしたものを、下に書いておきます。
<head>
<!-- class="fallback" と data-fallback="URL"を追加 -->
<!-- ロードに成功したらclassを外す -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"
onload="this.className='';" class="fallback" data-fallback="/path/to/local/bootstrap.min.css">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"
onload="this.className='';" class="fallback" data-fallback="/path/to/local/font-awesome.min.css">
<!-- 中略 -->
<script>
// DOMで書くのが面倒なので、jQueryにしています
$(window).on('load', fcuntion(){
$('link.fallback').each(function(){
var $elem=$(this);
$elem.attr('src', $elem.data('fallback'));
});
});
</script>
</head>