22
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

パブリックCDNからのロードに対して、フォールバックを行う

Posted at

何かと便利な、パブリック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>を読み出した場合、ロードと実行のタイミングは以下のようになります。

  • asyncdeferなしの場合…その場でロードして実行1
  • deferあり…DOMContentLoadedの前に、(deferどうしは順番に従って2)実行
  • asyncあり…onloadの前のどこかでロードして実行

ということで、これらに従って「正しく動作すれば読み込みが行われるはずのタイミング」でフォールバック処理を入れることとなります。

その場でロードが必要なもの

asyncdeferなしの場合、次の行に<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>

参考ページ

脚注

  1. ブラウザによっては最適化で先のファイルを読み始めることもありますが、動作としてはこう処理した時と同じ結果になります。

  2. 古いブラウザでは守ってくれないこともあるらしいです。

22
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?