Historyオブジェクトに触れたきっかけ
所属するシステム開発プロジェクトの要件に、Webサイトの「総アクセス数」や「ページ別アクセス数」等を取得すること、というものがありました。
当初はNew Relicエージェントをアプリケーションに組み込み、自動でアクセス情報を取得する予定でしたが、課題が発生して断念。。代替策として、エージェントを用いずにアクセス情報を取得するために、イベントAPIを用いてカスタムイベントデータをNew Relicに送信する独自実装を行うことになりました。この実装を実現するための手段がHistoryオブジェクトだったのです。
参考ページ
イベントAPIを使用したカスタムイベントの送信
Historyオブジェクトとは?
Historyオブジェクトは、JavaScriptに標準搭載されているブラウザの履歴情報を管理するためのオブジェクトです。Historyオブジェクトを使用することで、ユーザのブラウザ履歴に関連する操作を行うことができます。
代表的なプロパティやメソッドは以下の通りです。
// 現在の履歴エントリー数を取得
const entries = history.length;
console.log(entries); // 履歴のエントリー数が表示される
// 一つ前の履歴エントリーに戻る(ブラウザの「戻る」ボタンと同じ動作)
history.back();
// 一つ後の履歴エントリーに進む(ブラウザの「進む」ボタンと同じ動作)
history.forward();
// 2つ前の履歴エントリーに戻る
history.go(-2);
// 3つ後の履歴エントリーに進む
history.go(3);
pushStateメソッドとreplaceStateメソッド
今回のアクセス情報取得のために用いたのがpushStateメソッドとreplaceStateメソッドです。この2つのメソッドを使用することで、ブラウザの履歴スタックに新しいエントリーを追加したり、既存のエントリーを置換したりすることができます。
history.pushState(state, title, url)
pushStateメソッドは、新しい履歴エントリーをスタックに追加します。
- state:追加する履歴エントリーに関連づける状態オブジェクト。履歴エントリーに関する情報を含めることができます。
- title:追加する履歴エントリーのタイトル
- url:追加する履歴エントリーのURL
history.pushState({ page: 1 }, "New Page", "newPage.html");
history.replaceState(state, title, url)
replaceStateメソッドは、現在の履歴エントリーを置換します。
- state:置換する履歴エントリーに関連づける状態オブジェクト。履歴エントリーに関する情報を含めることができます。
- title:置換する履歴エントリーのタイトル
- url:置換する履歴エントリーのURL
history.replaceState({ page: 2 }, "Replace Page", "replacePage.html");
実装内容
上述したpushStateメソッドとreplacePlaceメソッドを用いて、クエリパラメータを含むURLの変更を伴う画面遷移等のイベントが発生した際に、ブラウザの履歴を正しく管理することができます。ブラウザの履歴を正しく管理できれば、Webサイトの「総アクセス数」や「ページ別アクセス数」も取得できそうですね。
プロジェクトではフロントエンドの開発にReactを使用しているため、ルートコンポーネントでuseEffectをフックし、サイトアクセス後のURLの変更を伴うすべてのイベントをNew Relicに送信するよう実装しました。
useEffect(() => {
// history.pushStateとhistory.replaceStateをオーバーライド
const overrideHistoryState = async function (originalMethod, ...args) {
// オリジナルのメソッドを呼び出す
originalMethod.call(history, ...args);
const pathName = window.location.pathname;
const queryParams = window.location.search;
// アクセス情報をバックエンドに送信
await postPageAccess(pathName, queryParams);
}
// オリジナルのメソッドを保存
const originalPushState = history.pushState;
history.pushState = async function (state, title, url) {
await overrideHistoryState(originalPushState, state, title, url);
}
// オリジナルのメソッドを保存
const originalReplaceState = history.replaceState;
history.replaceState = async function (state, title, url) {
await overrideHistoryState(originalReplaceState, state, title, url);
}
}, [])
おわりに
今回は、アクセス情報取得について、フロントエンドのみ記載しましたが、バックエンドの処理も必要ですし、New Relic側でのデータのビジュアライゼーションの問題もあります。New RelicにはSQLライクな記述で生成可能な強力なダッシュボード機能があるのですが、それらについてはまたどこかで。。
お読みいただきありがとうございました。