Blogger:表示速度向上のためCSSとJavaScriptの置き場を考える:その2

表示速度の改善にはDOM、CSSOM、JavaScriptの実行の依存関係を考えないといけません。JavaScript のインタラクティブ機能を追加する | Web Fundamentals - Google Developersを読むとJavaScriptを非同期にするとパーサーブロックがかからないので表示速度の改善が見込めるようです。

前の関連記事:Blogger:表示速度向上のためCSSとJavaScriptの置き場を考える:その1


DOM、CSSOM、JavaScriptの実行の依存関係


オブジェクト モデルを構築する | Web Fundamentals - Google Developers

DOMとCSSOMについてはこのページの解説がわかりやすいです。

ブラウザはHTMLタブを読み込んでDOM(ドキュメント オブジェクト モデル)を構築することによって、NodeとしてHTMLの要素にアクセスできるようになります。

ブラウザはCSSを読み込んでCSSOM(CSS オブジェクト モデル)を構築して、それをNodeに適用します。

JavaScriptはDOMやCSSOMの追加や変更、削除ができます。

このようにDOMとCSSOMとJavaScriptは相互に関係しています。

HTML パーサーが script タグに遭遇すると、DOM 構築のプロセスを一時中断し、JavaScript エンジンに制御権を渡します。JavaScript エンジンの実行が完了すると、ブラウザは中断前の位置から DOM 構築を再開します。
JavaScript のインタラクティブ機能を追加する | Web Fundamentals - Google Developers
インラインJavaScriptに限らず<script> タグのsrcで指定したjsファイルであってもJavaScriptの実行が完了するまでDOM構築が中断されるのは同じことです。

さらに後者の場合はsrcで指定したjsファイルを読み込みのオーバヘッドを生じるのでインラインJavaScriptよりもさらに実行が遅れることになります。

さらにJavaScriptの実行はCSSOMの構築が完了するまで遅らされるためDOM構築はさらに遅れることになります。

JavaScriptの読み込み位置をページの最後にするだけでは不十分


HTMLの末尾で読み込ませれば、ページの描画が全て終わってからJavaScriptファイルが読まれるため、(JavaScriptファイルの読み込み完了を待つために)描画が止まることはありません。
2/2 外部CSSと外部JavaScriptで表示速度の低下を防ぐには [ホームページ作成] All About
CSSOMを含めてDOMの構築をブロックするのがJavaScriptの読み込みだけでなく、実行の完了、ということになれば単純にscriptタグをHTMLの末尾に置けば解決ということにならないことになります。

先ほど参考にしたJavaScript のインタラクティブ機能を追加する | Web Fundamentals - Google DevelopersにはJavaScript はDOMとCSSOMの変更を行うことができるために、ブラウザは明示的に非同期化宣言をしていないJavaScriptの実行が完了するまでDOMの構築をブロックします、と書いてあります。

つまりブラウザは「JavaScriptファイルの読み込み完了を待つ」のではなく「JavaScriptの実行が完了」を待つのです。

「読み込みの完了を待つ」のであれば読み込みのオーバーヘッドを回避するために直接JavaScriptをHTMLに書き込むインラインJavaScriptにすればよい、と考えてこのブログではBlogger:Googleサイトの利用をやめて表示速度を改善するでほとんどインラインJavaScriptにしました。

それによって表示速度も改善しましたが、読み込み速度が改善したなら、実行完了するまでの時間も短くなるのでこの結果ではDOMの構築をブロックしているのがJavaScriptの読み込みなのか実行なのかはわかりません。

振り返ってみるとBlogger:ページ表示速度を計測して速度向上対策を考えるで計測に使ったGTmetrix | Website Speed and Performance Optimizationの判定結果では、Defer parsing JavaScript to reduce blocking of page rendering.となっており、JavaScriptをパースするのを遅らせるように推奨されました。

インラインJavaScriptもパースを遅らせるように指摘されているので、「パース」は「読み込み」ではなく「実行」を指しているようです。

Blogger:ページ表示速度を計測して速度向上対策を考えるですでに自分でちゃんと「遅延実行」と書いていますね、、、「遅延実行」は「遅延評価」と紛らわしいので用語の使い方としてはいまいちですが。

ちなみにBlogger:表示速度向上のためCSSとJavaScriptの置き場を考える:その1で測定に使ったPageSpeed Insightsのモバイル版の改善策としてインライン化が推奨されているのは、スクロールしないと見えないコンテンツのためのCSSの読み込みとJavaScriptの実行を排除するためです。

外部サイトのJavaScriptはasyncで非同期化してDOM構築を妨げないようにする


async キーワードを script タグに追加することで、ブラウザに対して、「スクリプトの準備が整うのを待つ間、DOM 構築をブロックしないように」と伝えることができます。この方法は、パフォーマンスの大幅な改善につながります。
JavaScript のインタラクティブ機能を追加する | Web Fundamentals - Google Developers
scriptタグのsrcで読み込んでいるJavaScriptについてはasyncとつけるだけです。

ただし非同期にすると実行される順番が保証されないので他のスクリプトで使うjquery.min.jsに使うのはやめておきます。(非同期に読み込むローダーはあるようです。Load jquery.js async - possible? - jQuery Forum

MathJax(1)ブログに数式をきれいに表示させる方法で導入したMathJax.jsにasyncを追加してみます。

MathJax.jsはテンプレートのHTMLに直接書いてあります。

XML の解析中にエラーが発生しました。行 233、列 15: Attribute name "async" associated with an element type "script" must be followed by the ' = ' character.

asyncだけscriptタグに追加すると=を続けないといけないと言われます。

async=true、でもダメです。

2013-08 | クリボウの Blogger Tipsに載っていたasync='async'もダメです。

このブログに貼り付けているGoogleアドセンスのscriptタグにはasyncがついてますが、HTMLの特殊文字(HTML-特殊文字)はエスケープしています。

テンプレートのHTMLに直接貼り付けずにHTML/JavaScriptガジェットに貼り付けてもよいのですが、Bloggerのエディタで簡単にエスケープできるので、エスケープしてテンプレートのHTMLに直接貼り付けることにしました。

エスケープするのはBloggerの投稿画面の作成モードに貼り付けてHTMLモードをみるとエスケープされていることがわかります。

ただし改行があると<br>がついてくるのでこれは手動で取り除かないといけません。
&lt;script async src='http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML' type='text/javascript'&gt; &lt;/script&gt;
これをHTMLテンプレートに貼り付けてうまくいきました。
<script type='text/x-mathjax-config'>
  MathJax.Hub.Config({tex2jax:{inlineMath: [[&#39;$$$&#39;,&#39;$$$&#39;]]}});
  MathJax.Hub.Config({tex2jax:{ displayMath: [[&#39;$$&#39;,&#39;$$&#39;]]}});
</script>
このMathJaxの設定部分はMathJax.jsを読み込んだあとに実行されないといけないので、非同期で読み込んだ場合はどうなっているのかと思いましたが、よく見たらこのscriptタグのMIMEタイプがtext/javascriptではなくtext/x-mathjax-configになっています。

接頭辞x-は標準化されていないMIMEタイプに対してつけられるもので、x-mathjax-configはMathJax独自のMIMEタイプになります。(JSの MIME-Type は text か application か、x- は必要か – カラクリ.jp)

独自のMIMEタイプはパーサーに無視されるので実行されません。

MathJaxではこのMIMEタイプがx-mathjax-configになった部分をMathJax.jsで、document.getElementsByTagName("script");を使って取得したscriptタグ一覧からtypeがx-mathjax-configであるものを探してinnerHTMLでこのタグの内容を取得していました。

そこからどう処理しているのかはよくわかりませんでした。

HTML Standard 日本語訳にも同じようなscriptタグの使い方の例がデータブロックとしてでてきます。

codeigniter - Lazy loading JavaScript and Inline JavaScript - Stack Overflow

これにはまずscriptタグのtypeをtext/delayscriptにしといて、実行するときにtext/javascriptに置き換える方法が載っています。

text/delayscriptは非標準のMIMEタイプなのでtext/x-delayscriptとするべきですね。

非同期広告コードについて - AdSense ヘルプ

Googleのアドセンスではadsbygoogleというグローバル変数の配列にオブジェクトを格納してそれをスクリプトの中からArray.prototype.shift()で切り出して設定値を取得していました。

外部サイトのJavaScriptの非同期化に付随するインラインJavaScriptはこれらのいずれかの方法で対応できそうです。

インラインJavaScriptを非同期化する方法とその効果


Bloggerでは同じドメインにファイルを置くことができないので、JavaScriptはインライン化するか読み込むオーバーヘッドが大きい外部ドメインに置くしかありません。

なのでBloggerではインラインJavaScriptを外部サイトのjsファイルに出して単純にasyncとつければ解決というわけにはいきません。

インラインJavaScript自体を非同期にする方法を考えてみます。

asm.js の非同期スクリプト - ゲーム開発 | MDN

スクリプトで動的に追加したスクリプトは非同期になるということです。

私がscriptタグについて知っていること全て : 知られていない属性や実行順序など | プログラミング | POSTD

ということでこのように動的にインラインJavaScriptを追加すれば非同期になるはずです。

先ほどでてきたscriptタグのMIMEタイプを後からtext/javascriptに変更する方法でもうまくいきそうです。

そもそもインラインJavaScriptの場合はhtmlファイルと同様に読み込まれるので、jsファイルのように読み込みを非同期に行ってその間に他のことをするということはないので、非同期にするメリットは時間のかかるJavaScriptの実行の完了を待たずにDOM構築を進めるということになります。

参考にしたサイト


JavaScript のインタラクティブ機能を追加する | Web Fundamentals - Google Developers
DOMの構築、CSSOMの構築、JavaScriptの実行の相互の関係についての解説。

オブジェクト モデルを構築する | Web Fundamentals - Google Developers
DOMの構築とCSSOMの構築の関係についての解説。

2/2 外部CSSと外部JavaScriptで表示速度の低下を防ぐには [ホームページ作成] All About
「非同期」ができなければこの方法でよいかもしれません。

Scripts Top vs. Bottom
JavaScriptを上におくか下におくかの速度比較の例。

GTmetrix | Website Speed and Performance Optimization
ページ表示速度測定サイト。

PageSpeed Insights
ページ表示速度測定サイト。

Load jquery.js async - possible? - jQuery Forum
jquery.min.js自体を非同期に読み込むのは簡単にはいかないようです。

2013-08 | クリボウの Blogger Tips
BloggerのテンプレートのHTMLのscriptタグにasyncをつけるには特殊文字をエスケープする必要がありました。

HTML-特殊文字
Bloggerの投稿作成画面の作成モードに貼り付けてHTMLモードでみるとエスケープできました。

JSの MIME-Type は text か application か、x- は必要か – カラクリ.jp
独自MIMEタイプは接頭語にx-をつけます。

HTML Standard 日本語訳
scriptタグのMIMEタイプを独自のものにするとブラウザにパースされなくなります。

codeigniter - Lazy loading JavaScript and Inline JavaScript - Stack Overflow
scriptタグにあらかじめ独自MIMEタイプをつけておいて非同期読み込みする方法。

非同期広告コードについて - AdSense ヘルプ
非同期に読み込むJavaScriptにグローバル変数を渡しています。

即時関数をcall(this)で呼ぶことについて - Qiita
AdSenseのJavaScriptはcall(this)で呼ばれていました。

Array.prototype.shift() - JavaScript | MDN
Array.prototype.pop()とは逆に前から配列の要素を切り出します。

asm.js の非同期スクリプト - ゲーム開発 | MDN
スクリプトによって生成されるスクリプトはデフォルトで非同期です。

私がscriptタグについて知っていること全て : 知られていない属性や実行順序など | プログラミング | POSTD
インラインJavaScriptを非同期に実行する方法。

次の関連記事:Blogger:表示速度向上のためCSSとJavaScriptの置き場を考える:その3

PR

2 件のコメント:

  1. はじめまして。bloggerのカスタマイズについて質問です。

    bloggerのトップページに記事の一覧を表示しないようにするにはどうすれば出来ますでしょうか?

    もしご存知でしたら教えて頂けないでしょうか。よろしくお願いします。

    返信削除
    返信
    1. インデックスページに投稿を表示させない方法ということでしょうか?
      Blogger:テンプレート編集(9)インデックスページをモバイルサイトのものに統一するではpostインクルードを置き換えてインデックスページの投稿一覧をモバイルサイトのものに変更しています。
      postインクルード自体をコメントアウトすれば目的は達成できるかもしれませんが、それだとブログのトップページの目的は果たさなさそうな気がしますけど。

      削除