概要
「ページの表示速度をあげるため、JavaScriptやCSSファイルがレンダリングを妨害しないようにする」
という目的にたいして調べたことをまとめる。
分析ツール
ページ表示速度を測定するために Lighthouse を利用しており、
分析結果の Eliminate render-blocking resources
を減らすことがゴール。
Lighthouseは、Chrome検証モードのAuditsタブからも利用できる。
PageSpeed Insights(PSI)はlocalやstaging環境では利用できないが、
PSIの分析エンジンとして Lighthouse を使用しているため、Lighthouse を利用すると参考にしやすいかも。
(Lighthouse と PSI は、以前は異なる仕組みでパフォーマンスを測定していたため、チェックする指標は同一ではなく、同じ指標だったとしても計測結果が異なることがあったらしい。)
Lighthouseの監査基準
PageSpeed Insights、Lighthouse の使用を開始しました
レンダリングとは
簡単にいうと、HTMLなどの文字データを読み取って、文字ではなく画面や画像を表示すること。
ブラウザは、ページを表示する前にHTMLを解析して DOM ツリー(xmlやhtmlドキュメントをツリー構造として表現したもの)を構築する。
この処理の途中でスクリプトが存在すると、HTMLの解析を一旦停止してスクリプトを読込・実行するため、
ページが最初に表示されるまでに時間がかかる。
レンダリングを妨害しないようにするには
JavaScript
こういうコードがあったとき、
<script src="common.js">
async属性をつけると、スクリプトは非同期に読込・実行される。
<script src="common.js" async>
defer属性をつけると、HTMLの解析完了後かつ DOMContentLoaded が発生する前に実行する。
実行を遅延(defer)させているイメージ。
また、この属性を付与すると、記述した順番で実行されるため、
ライブラリなど依存するファイルがある場合は、defer属性を用いて親ファイルから記述していけばよい。
<script src="jquery.js" defer>
<script src="common.js" defer>
<script src="header.js" defer>
async / defer 検証の違いを検証してみる
以下のHTML、JSを作成して検証する。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>実行順序テスト</title>
<script src="js1.js" defer></script>
<script src="js2.js" defer></script>
<script src="js3.js" defer></script>
</head>
<body>
</body>
</html>
console.log(111)
console.log(222)
console.log(333)
結果
asyncだと、リロードするたびにバラバラ。
deferだと、何回リロードしても、111、222、333の順でconsoleに表示される。
scriptは </body>
の直前に書けば先にHTMLが分析されてページ表示速度が早くなるという情報がネットにあり、そういうものと思っていたが、
Lighthouse で分析してみるとまったく関係なかった。
どこに記述しようが async/defer 属性が無ければHTMLの解析を止めてスクリプトを読み込むため、
Eliminate render-blocking resources
にカウントされる。
MDN script : スクリプト要素
MDN HTMLScriptElement
CSS
こういうコードがあったとき、
<link rel="stylesheet" href="style.css">
rel
属性に preload
を使用すると、非同期で読み込まれる。
as
属性にリソースの種類を指定しなければならない。
<link rel="preload" href="style.css" as="style">
※ pleload
は、Firefox等、対応していないブラウザがあるため、使用の際は注意する。
今回はFirefoxにも対応する必要があったため利用しなかった。検証も行っていない。
MDN rel="preload" によるコンテンツの先読み
詰まったこと
- 自分で試すという選択肢がなかったこと
簡単に確認できる内容だけど、
調べている最中は、async/deferの動作に確信が持てず、混乱したので先輩に助けを求めた。
「適当にhtmlとjs作って試してみたら?」
と言われて、た、たしかに・・・!と思った。
詰まったら大体、「公式サイトを確認する」「ネットで調べる」「誰かに聞く」ので、
自分で試すことを思いつかなかった。
- ブラウザの開発者ツールでエラーを確認できることを忘れていたこと
ひと通り調べた後、試しにすべてのJSファイルにasync属性をつけたり、逆にすべてにdefer属性をつけたが、
ヘッダーが表示されなくなったり、スライドが動かなくなったりした。
ということは、ライブラリのような、他に依存されているJSが先に(非同期に)読み込まれることにより、正常に動作しなくなったと考えられる。
今回対応した案件では、
HTMLを描画しているJSがライブラリや他のJSに依存しているので、ライブラリなど一部のJSを非同期に読み込むことができない。
(JSでHTMLを描画するのはどれくらい一般的なんだろう・・・。)
async または defer を付加すると次のようなエラーが出ていたので、consoleに注目していればすぐ気づいたかもしれないが、
consoleを見る習慣がなかったのですぐに気づけなかった。
header.js:87 Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.
header.js:87「ドキュメント」で「書き込み」を実行できませんでした:明示的に開かない限り、非同期にロードされた外部スクリプトからドキュメントに書き込むことはできません。