<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行。シンプル。
で、他のソーシャルボタン系も書き換えられないかと思ってやってみた。読み込むパターンはだいたい同じなので、そんな大した労力ではないはず。
<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>
どうしようかなと思ったのだけど、なくても自動挿入されるので省いちゃった。
<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があるけど、あれも読み込み部分は書き換えられそう。