wordpressで読み込まれているjavascriptにdeferもしくはasyncをつける

大体の場合、wordpressから読み込まれているスクリプトはwp_enqueue_scripts()などで読み込まれています。その際にスクリプトを識別するためのハンドルなるものをつけられています。

例えばプラグイン作者は自分がどんなハンドルをつけたのかはわかっているので、それをうまく使ってjQueryが読み込まれたらプラグインのjsも読み込まれるようにしようとかと計画が練れるわけですが、一般的に利用する場合、そのハンドルは何なんだよと言うことになります。

htmlのソースをデベロッパーツール等で見ると、<script>タグにそのスクリプトは読み込まれていて、GoogleのPageSpeed Insightsなんかでレンダリングブロックしているjavascriptを排除してくださいと怒られるわけです。
実際、レンダリングブロックがある場合は、そこで読み込みが止まっていたりするのでページの表示までに時間がかかります。

ここらはもっと詳しく詳細に書いてあるサイトに任せて、レンダリングブロックをどのようにすれば無くすことができるのかを考えた時に、通常は、<script>にasyncなりdeferなりを入れるだけと言うのに気が付きます。ではどうすればそれらを入れられるかですが、以前以下のようなスクリプトを紹介しました。

script_loader_tagと言うフックで全ての<script>にdeferを入れると言うものでした。deferを入れると言うかtype=’text/javascriptとある部分をdeferで置き換えると言うようなものです。

これで特に問題はなかったわけですが、asyncとdeferを使い分けられたらもっと速くなるんじゃね?と思いまして、スクリプトを書いてみました。

asyncとdefer

絶対的な注意点として、スクリプトにdocument.write()が使われている場合は、これらを使ってはいけません。そもそもdocument.write()は2010年の段階で非推奨になっているので最近のスクリプトなら使っていることはまぁ無いはずですが、一部これらをまだ使っているものもありますので事前に必ずチェックしてください。

また「依存関係のあるスクリプト同士には defer を、なければ async を付けることができます。」これは実行順序が大切で、何かしらが読み込まれていないと動作できないものに関してはdeferと言う感じです。jQueryが読み込まれていないのにそこに依存するスクリプトをasyncで読み込めば、asyncは読み込み準備ができた際にすぐに非同期で読み込むのでまだjQueryが読み込まれていない場合も考えられます。

詳細は async/defer 属性と DOM 構築 など参照。

プラグインも作っておきました

asyncとdeferを区別するために、そのスクリプトが読み込まれているハンドルがわかると便利じゃないの?と言うことで、プラグインも作りました。と言ってもプラグイン形式にしてあるだけで、中身はただのアクションフックです。

プラグインの中身はこんな感じです。アドミンバーが出ている時のみ画面上部に読み込まれているスクリプトのハンドルが表示されます。
プラグイン形式にしてあれば、ダッシュボード→プラグインで、有効・無効を切り替えるだけですし、子テーマfunctions.phpに書いてコメントアウトしたりまたそれを解除したりする必要もありません。必要なくなれば、プラグインから無効にすれば良いだけです。

これらを元にハンドルがわかったわけですから、それを仕分けるスクリプトを書きます。これは子テーマfunctions.phpに書きます。本来ならプラグインからチェックできればよいわけですが、ダッシュボードといわゆる記事部分とで読み込まれているスクリプトが異なったり、もしくは記事部分で読み込まれているハンドルだけがわかればよいのですがそれを知る方法がまだつかめず設定画面等で簡単に書き換えると言うのができません。
これができるとまあまぁ便利なプラグインなんですけどね(笑)

ひとまず、こういう理由から別途テキストエディターなどでスクリプトを書きます。

サンプルスクリプト 子テーマfunctions.phpに追記

まぁ別に大したスクリプトではないのでだいたいすぐに理解してもらえると思いますが、$async_scriptsと$defer_scriptsにそれぞれ[deferなりasync]をつけたいスクリプトのハンドルを、detect-script-tagプラグインを有効にしておいて、記事画面等に表示されるハンドルを調べ分別するだけです。
で、保存してftp等で子テーマディレクトリにアップロード。

それまで「全てdefer挿入」時に10秒ぐらいの読み込み時間だった記事が、上記スクリプト適用後6〜7秒あたりに改善されました。ただし試行回数が少ないので、おそらくはネットワークの速度やその他諸々の誤差の範囲かと。

さてここらで問題点でも

async/defer属性を入れることでレンダリングブロックが防げると言うのはなんとなしに理解してもらえたかと思います。またそれによってページの読み込み速度も多少なりとも速くなっているかと思います。

よく、スクリプトは閉じbodyタグの直前にと書いてあるサイトがあります。Simplicityでも例に漏れずこのようにjQueryは閉じbodyタグの直前に書いてあります。しかしasync/deferを入れると読み込めるようになった段階でなるべく速く読み込むのがasyncで、deferはDOM解析後後回しで読み込みますから、閉じbodyタグの直前とかに書かなくてもheadで良いんです。

実行順序が問題なければheadでやればHTMLのレンダリングと平行して処理してくれるというような記事も見たことがあります。

ここから、もしこれが正しいのであれば、jQueryはheadで読み込んでおき、並列に処理しながら、それに依存するスクリプトはdeferでDOM解析後に読み込み、これらとは全く別で依存関係の無いもの、例えばGoogle アナリティクスやfacebook sdk、あるいはtwitterのwidget.js等こういったものはページの終わり辺りで読み込めば良いと言うことになります。

まずサイトを表示するのが先決で、それを邪魔させないと言うことからasync/deferを使うわけです。しかし、facebookを利用していないのにそのSDKを読み込む必要はないですよね。twitterのwidget.jsもそうです。たしかにこれらはサイトの表示の一部ですが、これらはスクリプトが動作しない場合はリンクであったり、blockquoteなどで表示されているはずです。

ページにアクセスした瞬間にこれらを書き換える必要性はなく、ページが表示された後にこれらリンク等を本来の状態に置き換えればよいわけです。つまりはlazyloadです。

lazyloadで言うと、画面内に入ったら代理のものと置き換えると言う仕組みですが、それだと問題が出ることもあり、それならばDOM解析後(document.ready)ではなく、ウィンドウ表示後(window.onload)の時に処理すれば良い感じになると思います。

一番良いのは画面内に要素が入った時に処理ですが、今度はサックリと読めないと言う箇所が出てくる可能性もあるので、できるだけファーストビューから外して、window.onload後2〜5秒後あたりに変換すれば、ページを読み進んでいる最中に変換が行われて丁度よいのではなかろうかと。

これらは他のスクリプトとの兼ね合いもあるので色々と考える所が多いのは確かです。

Simplicityの場合の修正案として

footer-javascript.phpの「Facebookページの「いいね!」ボタン用のコード」部分で、

とすれば、非同期に読み込みます。

同ファイル「animatedModal.jsの呼び出し」部分から、

とすれば必要な箇所のhtml構造ができてから実行されると思います。問題なければasyncでも良いかもしれません。

同ファイルで「ソースコードのハイライト表示」部分から、

とすれば必要な箇所のhtml構造ができてから実行されると思います。

footer-slicknav.phpにて、

これも問題がないようであればasync、問題が出るようならdeferで。

ひとまず目についたのはこれぐらいでしょうか。これらは利用される時のみ変更すればよいです。利用していないのに修正しても読み込まれなかったりしますので面倒くさいだけですので。

ついでにjQueryはキャッシュしちゃうか

jQuery3.0.0をCDNから読み込んで一時的にキャッシュします。jQuery3.0.0ではマズイとかの場合はバージョンを書き換えてください。またCDNをムリに使う必要はありませんし、jQuery3.0.0にすると、それまでのバージョンでは問題なかったことが新たに問題になる場合もありますので、それら問題を自力で解決できる人のみと言うことで。

以前のバージョンでも同じようにキャッシュしてwordpress自前のjQueryを使わないと言う手段が取れるので必要がある方は試してみてください。

まとめ

今回はasync/deferについて書きましたが、javascript等は導入しているプラグインの違いや利用状況が異なるために、こうすれば最適と言うようなことはなかなか提示できません。なので、ほぼ自前で改造してもらうことになりますが、何もしていないデフォルトの状態よりは悪くなることはないだろうと思います。

テスト方法として、async/deferどちらを使えばわからないと言うような場合は、asyncから試してみて問題が出ればdeferに変えると言うことでよいかと思います。
何にも依存していない単体のスクリプトで、かつdocument.write()が使われていなければasyncで間違いないだろうと思います。

エラーの状況や、その修正手段としてデベロッパーツールは見れるように、また何かしらのエラーが出た場合は対応できるように、ftpを利用してバックアップを取りながら十分に注意して作業を進めてください。

スポンサーリンク

シェアする

フォローする