uhyohyo.net

JavaScript初級者から中級者になろう

十五章第四回 Resource Timing

このページの最終更新日:

前回の続きです。前回はPerformanceEntryオブジェクトの扱い方を紹介しました。今回はその具体例です。

Resource Timingという仕様では、Webページからimg要素やlink要素によって読み込まれたリソースについて、それにかかった時間などを知ることができます。これは、一五章第二回で紹介したnavigation Timingと似ていますね。あちらはページ本体に読み込みにかかった時間であり、本体が読み込み完了してHTMLのパース・DOMの構築まで終わったら読み込み完了としていたので、外部リソースに読み込みにかかった時間までは測れませんでした。

PerformanceResourceTiming

Resource Timingでは、ページから読み込まれた外部リソース一つ一つについてPerformanceResourceTimingオブジェクトが作成され、window.performanceに登録されます。このPerformanceResourceTimingというのは、前回紹介したPerformanceEntryを継承したオブジェクトです。この場合、前回紹介した4つのプロパティは次のような値が入っています。

name
nameプロパティには、リソースのURLが入っています。
entryType
entryTypeプロパティは"resource"で固定です。これは、このPerformanceEntryがPerformanceResourceTimingであることを示す値になっています。
startTime
redirectStartが有効な場合はその値、そうでなければfetchStartの値です。(詳しくは後述)
duration
responseEndとstartTimeの差です。(これも後述)

リソースのURLと言っていることから分かるとおり、ひとつのリソースに対してひとつのPerformanceResourceTimingオブジェクトが作られます。

このページもCSSなどを読み込んでいますので、試しにコンソールで次のコードを実行してみると、PerformanceResourceTimingオブジェクトがいくつも入った配列が得られるのがわかります。他のオブジェクトも多分ありますが。


console.log(performance.getEntriesByType("resource"));

PerformanceResourceTimingには、さらに以下のプロパティがあります。

initiatorType
そのリソースが何によって読み込まれたか。HTML要素ならばその要素名("link"とか)、CSSならば"css"、XMLHttpRequestならば"xmlhttprequest"になる。
redirectStart
リダイレクトがあった場合、一番最初のリソースにアクセスした瞬間の時間です。リダイレクトが無かった場合は0です。
redirectEnd
リダイレクトがあった場合、最後のリダイレクトページから受信を完了した瞬間の時間です。リダイレクトが無かった場合は0です。
fetchStart
リソースへのアクセスを開始した瞬間です。これは厳密には、URLの文字列解析を開始する瞬間と(多くの場合)一致します。
domainLookupStart
URLの解析が終わり、DNSサーバーに問い合わせを開始した瞬間です。
domainLookupEnd
DNSサーバーとの通信が終了した瞬間です。
connectStart
リソースのあるサーバーへの通信を開始した瞬間です。
connectEnd
サーバーへの通信を確立した瞬間です。
secureConnectionStart
HTTPS通信の場合、ハンドシェイクを開始した瞬間です(ブラウザによってはサポートしないこともあるようです)。HTTPS通信でない場合は0です。
requestStart
サーバーへリクエストを送信し始める瞬間です。
responseStart
サーバーからレスポンスの最初の1バイトを受信した瞬間です。
responseEnd
サーバーからレスポンスを最後まで受信した瞬間です。

Navigation Timingの話を思い出しますね。まあ、この情報を全て使いこなす必要はないでしょう。結局、そのリソースを取得するのにどれだけ時間がかかったかを調べるには上で紹介したdurationプロパティの値を見れば十分なわけです。より詳しい情報が必要になったときに他のプロパティを見ればよいでしょう。

注意点がひとつあります。それは、時刻を表すプロパティがたくさん出てきましたが、今回これらはUNIX timeではないということです。これらは、performance.timing.navigationStartの時刻を0とするミリ秒単位の時間で表されます。なので、これらの値を利用して実際の時刻を求めることは一応可能であるものの、基本的には時刻の情報は2つの値の差をとって経過時間として使うことになります。

また、initiatorTypeプロパティについて補足しておくと、例えばimg要素で画像を読み込む場合はこの値は"img"になるし、link要素でCSSファイルを読み込む場合は"link"です。また"iframe"などもありえます。CSSのbackground-imageなどで画像ファイルを読み込む場合は、CSSファイル経由で読み込まれているので"css"ですね。

ただ、今回もやはり他のオリジンの情報には厳しくて、他のオリジンのリソースの場合にはfetchStartとresponseEndの値以外は軒並み0になってしまいます。この2つが有効なのでdurationを得ることはできますが、他の値は役に立ちません。ただし、リソースを読み込む際にサーバー側がTiming-Allow-Originレスポンスヘッダを指定した場合は、他のオリジンであっても時間の情報を取得することができます。これは、十三章第一回で紹介したAccess-Control-Allow-Originヘッダと似ていますね。

Performanceオブジェクトの拡張

実は、PerformanceオブジェクトはResource Timing関連のメソッド等を持っています。

1つはclearResourceTimings(引数なし)です。これは、Performanceに登録されているPerformanceResourceTimingオブジェクトを消去してしまいます。

もう一つはsetResourceTimingBufferSizeです。これは引数に数値を指定することで、「最大何件までPerformanceResourceTimingオブジェクトを保存するか」を設定できます。

実は、デフォルトでは150件まで保存されることになっています。これは、やはり情報を保存するというのはメモリを食うので、何らかの形で制限する必要があるからでしょう。それだと困るという場合には、このメソッドでこちら側から数を増やしてやることができます。

また、実はPerformanceオブジェクトではイベントが発生することがあります。Performanceオブジェクトで発生するイベントの一つがresourcetimingbufferfullイベントです。これは、PerformanceResourceTimingが保存できる最大件数に達した場合に発生します。もし件数が足りない場合は、このイベントが発生したらそれにあわせてsetResourceTimingBufferSizeメソッドで実行するのがよいでしょう。

イベントなので当然addEventListenerでハンドラを登録します。


performance.addEventListener("resourcetimingbufferfull",function(e){
  //最大件数に達した場合の処理
});

いつものことですが、addEventListenerを使ってイベントを登録する方法のほかに、次のようにイベントハンドラを直接プロパティにセットする方法もあります。


performance.onresourcetimingbufferfull=function(e){
};

以上でResource Timingに関して紹介すべきことは終了です。最後にサンプルを用意しました。このページから読み込まれたリソースを列挙するサンプルです。


window.addEventListener("load",function(e){
  //ページ内に<div id="resultarea"></div>をあらかじめ用意しておく
  var div=document.getElementById("resultarea");

  var entries=performance.getEntriesByType("resource");
  entries.forEach(function(obj){
    //objはPerformanceResourceTimingオブジェクト
    var p=document.createElement("p");
    p.textContent=obj.name+"を"+obj.duration.toFixed(2)+"ミリ秒かけて読み込みました";
    div.appendChild(p);
  });
},false);

最初のloadイベントというのは、ページの読み込みが完了したときに発生するイベントでしたね。今回はリソースの読み込みについての情報が欲しいのでこのイベントを用います。

こんな感じでリソースに関する情報を取得することができます。

次回も関連した話です。