<script async>でJavaScriptの非同期読み込みを

JavaScriptファイルをスクリプトから非同期で読み込むパターンは古い、という話を目にしたのはもう半年前のこと。

言ったのはIlya Grigorik。月末のGoogle Japanでのイベント月初のHTTP2ConferenceにくるWebパフォーマンス界のすごいひとですよ。


件のパターンは、スクリプトで動的に script を生成して、そこに読み込みたいJavaScriptファイルをぶっこんで読み込むというもの。

<script>
    var script = document.createElement('script');
    script.src = "//somehost.com/awesome-widget.js";
    document.getElementsByTagName('head')[0].appendChild(script);
</script>

ところが、こうしてしまうとスクリプト中のURL(ここでは //somehost.com/awesome-widget.js)がブラウザのプリロードスキャナにひっかからないこと、そしてこのスクリプトの実行がCSSOMの構築を待つために、遅くなっちゃうんだと。

で、HTML5から導入された <script async> を使えば、非同期で読み込まれるし、プリロードスキャナの恩恵にも預かれるとのこと。サポート状況も良くなってきたので、そっち使ったほうがいいのではないかと。

<script src="//somehost.com/awesome-widget.js" async></script>

シンプル。古のスクリプト読み込みが戻ってきた感。こういうハックなしにいいことできるようになってほしいよね。

ソーシャルボタンの読み込み部分を書き換える

さて、このIlyaのエントリが公開されてからしばらくして、スクリプトで非同期読み込みをしていた+1ボタン<script async> を使うものに変わった。こんな感じになった。

<!-- スクリプト -->
<script src="https://apis.google.com/js/platform.js" async defer></script>

<!-- ボタン -->
<div class="g-plusone" data-annotation="none"></div>

実質2行。シンプル。

で、他のソーシャルボタン系も書き換えられないかと思ってやってみた。読み込むパターンはだいたい同じなので、そんな大した労力ではないはず。

Facebook

<div id="fb-root"></div>
<script>(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.0";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>

こうなった。

<script src="//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.0" id="facebook-jssdk" async></script>

<div id="fb-root"></div> どうしようかなと思ったのだけど、なくても自動挿入されるので省いちゃった。

Twitter

<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>

こうなった。

<script src="//platform.twitter.com/widgets.js" id="twitter-wjs" async></script>

プロトコルを判別するコードは、protocol relativeなURLにした。

はてなブックマーク

ボタンのページ見たら、すでに <script async> 使うようになってた。わーい。

<script type="text/javascript" src="http://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script>

関数を使った読み込みも書き換えてみる

身の回りでそれ系のボタンを使うときは、「ソーシャルボタンはお友達さ(・ω — MOL」にある非同期読み込みをする関数をベースにしたものを使っていた。

<div id="fb-root"></div>
<script>(function(d, s) {
  var js, fjs = d.getElementsByTagName(s)[0], load = function(url, id) {
    if (d.getElementById(id)) {return;}
    js = d.createElement(s); js.src = url; js.id = id;
    fjs.parentNode.insertBefore(js, fjs);
  };
  load('//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.0', 'facebook-jssdk');
  load('https://apis.google.com/js/plusone.js', 'gplus1js');
  load('//platform.twitter.com/widgets.js', 'tweetjs');
}(document, 'script'));</script>

これがこうなった。

<script src="//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.0" id="facebook-jssdk" async></script>
<script src="https://apis.google.com/js/platform.js" async defer></script>
<script src="//platform.twitter.com/widgets.js" id="twitter-wjs" async></script>
<script src="http://b.st-hatena.com/js/bookmark_button.js" async></script>

はてなを加えても4行。わーい。

以前だったかLikeボタンを設定する時に、読み込む関数のなかでパラメータを設定してた記憶がある。そうだったらちょっと面倒だなと思っていたのだけれど、最近のソーシャルボタン系はURLのクエリーやら data- 属性で指定できるのね。なのでとくに必要なかった。

もしある場合はどうしようかね。元のコードを見るしかないからどうも言えないか。


いまのところLikeボタンもTweetボタンもちゃんと動いているっぽい。他の非同期読み込み系だとGoogle Analyticsがあるけど、あれも読み込み部分は書き換えられそう。