Navigation Timingを軸にJavascriptの実行タイミングを理解する

恥ずかしながら`$(document).ready()`や`window.onload`、`document.addEventListener('DOMContentLoaded')`などそれぞれの実行タイミングの違いを正確に理解できていなかったのですが、調べていくうちにNavigation Timingを理解しておいたほうが後々捗りそうだと思い至り、Navigation Timing周辺の情報と合わせ調べた内容を記事にしてみました。

## このページの目的

Navigation Timingを念頭に置いた上でJavascriptの実行タイミングを把握できるようになること

## Navigation Timingとは

- [仕様](http://w3c.github.io/navigation-timing/)
- [実装(MDN)](https://developer.mozilla.org/en-US/docs/Web/API/Navigation_timing_API)

Navigation Timingとはブラウザがウェブサイトを表示するまでの間の一連のプロセスとタイミングを細かく定義したもので、`Unload`,`Redirect`,`Fetch`,`DNSLookup`,`TCPConnect`,`Request`,`Response`,`Processing`,`Load`という処理プロセスとなっており、それぞれの境目にもStart及びEndというタイミングがあります。
この実装がNavigation Timing APIで、Navigation Timing APIとはこれらのタイミング毎に到達した時間を、それぞれUnixTimestampで取得できるというものです。

## 図でイメージをつかむ

[W3C](http://w3c.github.io/navigation-timing/)より引用、Navigation Timingの全体像です。
このうち、Javascriptの実行においては特に`Processing`,`Load`のブロックに注目します。

![Screenshot_1.png](https://qiita-image-store.s3.amazonaws.com/0/66424/8e04a0b2-aad1-ad52-5d48-8d4228fb52e7.png)

続いて[Google](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp?hl=ja)より引用、`Processing`と`Load`の詳細が描かれています。

![dom-navtiming.png](https://qiita-image-store.s3.amazonaws.com/0/66424/4b3c3531-45d6-d5f8-724f-96b45d30840a.png)

Javascript観点では、

- DomLoading ([非推奨](http://w3c.github.io/navigation-timing/#dom-performancetiming-domloading))
- DomInteractive
- DomContentLoaded
- DomComplete
- Load

といった5つのタイミングとその流れを理解できればOKです。

## ブラウザが画面を構築するまでの流れ

Navigation Timingとは別に、ブラウザが画面を構築するまでの基本的な流れをおさえておくと理解が早まる気がしたので、ここで簡単に補足します。

Googleより引用。

![analysis-dom-css-js.png](https://qiita-image-store.s3.amazonaws.com/0/66424/575de614-29d4-eca0-141a-0a282cfb1d96.png)

Request送信後ResponseとしてHTMLドキュメントを取得したら、Parseが始まりDOM構築が開始されます。Parseする中でCSSリソースやJSリソースを発見し次第それらのParseも順次開始されます。そしてCSSOMが構築されたあとにJSの実行が始まります。

この場合、Render processのRequest pageまでに該当するのがNavigation Timingにおける`Unload`から`TCP`まで、各GETがNavigation Timingにおける`Request`、responseは`Response`を指し、1つ目のBuild DOMに当たるタイミングは`DomLoading`および`DomInteractive`と考えればよいでしょう。また、2つ目のBuild DOMと最後のRender Pageの境目が`DomComplete`に該当するかと思います。

## `Processing`,`Load`とブラウザの挙動の関係

以上を踏まえブラウザがWeb画面を表示するにあたっての処理の流れはおよそ以下のようになると思います。

|Navigation Timing|Browser|
|---|---|
|DomInteractive|HTMLドキュメントのParseを開始|
|DomContentLoadedEventStart|DomContentLoaded開始の合図|
|DomContentLoaded|HTMLドキュメントのParseが完了、CSS,画像等のサブリソースの読み込みやParseを開始|
|DomContentLoadedEventEnd|DomContentLoaded終了の合図|
|DomComplete|DOMとCSSOMが組み合わさり画面構築が完了、画像などの読み込みも完了している状態|
|LoadEventStart|Loadタイミング開始の合図|
|Load|Loadタイミングでの処理があれば実行|
|LoadEventEnd|Loadタイミング終了の合図|

## 実行タイミングやreadyStateと照らし合わせる

前置きが長すぎました…では実行タイミングはどう捉えておけばいいのかというのを、記法別に、また`document.readyState`の戻り値も合わせて並べてみます。

|How to Call|Navigation Timing|readyState|
|---|---|---|
|`<head>`内で`<script src="app.js"></script>`|DomInteractive|loading|
|`<head>`内で`<script async src="app.js"></script>`|DomContentLoaded|interactive|
|`</body>`手前で`<script src="app.js"></script>`|DomInteractive|loading|
|`</body>`手前で`<script async src="app.js"></script>`|DomContentLoaded|interactive|
|`document.addEventListener("DOMContentLoaded")`|DomContentLoaded|interactive|
|jQueryを使って`$(document).ready`|Load|complete|
|`<body>`内でダイナミックロードする `var script = document.createElement('script');script.src = 'app.js';document.body.appendChild(script);`|DomContentLoaded|interactive|
|`window.onload`|Load|complete|

手元の環境で動作を確認した限りではあるので多少間違い、勘違いはあるかもしれませんが、大まかな流れはイメージできるかなと思います。

## おわりに

はじめは興味本位で調べ始めた程度だったのですがもしかしたら他の人にとっても有用かもしれないと思い、簡単にですが記事に起こしてみました。もし間違い等がある場合には指摘いただけると有難いです。

## 参考

- <http://w3c.github.io/navigation-timing/>
- <https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp?hl=ja>
- <http://d.hatena.ne.jp/jovi0608/20111209/1323428049>