シングルページアプリケーション(SPA)でページビューをトラッキングする方法

Posted on

こんにちは、カスタマーサクセスエンジニアで丘サーファーの白坂です。
HiCustomer は SaaS として提供されるカスタマーサクセス管理プラットフォームで、各テナントから送信されてくるログデータに基づき顧客のスコアリングやその他の集計処理、またそれらをトリガーとした通知の送信などを行います。
そのためHiCustomerではページビューを含むログデータを送信する一つの手段としてトラッキングコードを提供しています。いわゆるGoogleアナリティクス(以下GA)のタグのイメージです。

ページビュー(以下PV)はWebページが「閲覧された回数」「開かれた回数」と表現されることが多く、 もともとはページの読み込み時にPVを計測するタグを呼び出し計測をすることが多かったのですが、 SPAでは、はじめにページを読み込んで以降、SPA内での画面遷移は擬似的に行われるため下記のような問題にぶつかります。

  • どんなにアクティブに利用されていても閲覧された回数は1回となってしまう
  • どのように利用されていたかわからなくなってしまう

つまりSPAでは新しいページの読み込みページ遷移がないため、PVとしてカウントするトリガーを定義する必要があります。タブの切替やボタンをクリックなどですね。

本記事ではSPAでPVとしてカウントすることの多いルーティング時のPVトラッキング方法について紹介していきます。

PVをトラッキングする際のポイントは、以下の2つです。

  • どのページを閲覧したか
  • どのタイミングでトラッキングコードを読み込むか

どのページを閲覧したか

多くのPV計測タグでは特段指定をしない場合、document.location.pathnameやリクエストのrefererを元にパス毎にページのPVをカウントするケースが多いようです。

History APIのhistory.pushState()を利用してルーティングを行っている場合には履歴とリファラーのパスを更新して仮想的な画面遷移をさせます。

https://developer.mozilla.org/ja/docs/Web/Guide/DOM/Manipulating_the_browser_history#履歴エントリの追加と修正

そのため特段の指定をせずにPV計測タグを呼び出すことでルーティング後のパスのPVを計測することができます。GAの場合は下記のとおりです。

    gtag('config', 'UA-XXXXXXXXX-X');

ハッシュを利用しwindow.locationの値を変更することによるルーティングを行う場合にはwindow.location = "#foo"; のようにハッシュの変更しかできません。ハッシュの変更はdocument.location.pathnameへ反映されず、以下URLの通りリクエストのrefererにも反映されません。

https://developer.mozilla.org/ja/docs/Web/Guide/DOM/Manipulating_the_browser_history#履歴エントリの追加と修正

そのためページを指定してPV計測タグを呼び出す必要があります。GAの場合は下記のとおりです。

    gtag('config', 'UA-XXXXXXXXX-X', {'page_path':'/hoge#foo'});

どのタイミングでトラッキングコードを読み込むか

求める発火タイミングの要件は以下で考えます。

  1. 初回読み込み時
  2. URL遷移時
  3. ブラウザの履歴情報内での移動時(ブラウザの戻るや進むなど)

ハッシュによるルーティングを行っている場合には onhashchangeイベントを利用することで上記の発火タイミング要件2, 3で発火することができますが、初回読み込み時に発火されません。onhashchangeイベントと画面読み込み時にタグを読み込むことで上記の要件を満たすことができます。

    window.onhashchange = () => {
    	gtag('config', 'UA-XXXXXXXXX-X', {'page_path':location.pathname + location.hash});
    };

History APIを利用したルーティングを行っている場合

History APIを使用する場合には発火タイミングの要件1,2,3それぞれを考慮する必要があります。

まず、初回読み込み時にPV計測タグを読み込み、URL遷移時に発火させるためにhistory.pushstate()呼び出し処理と合わせてPV計測タグを読み込みます。

そしてブラウザの履歴情報内での移動時(ブラウザの戻るや進むなど)に発火させるためにpopstateイベントを利用します。

    window.onpopstate = () => {
    	gtag('config', 'UA-XXXXXXXXX-X');
    };

これでやっと要件を満たすことができました。

history.pushState() 又は history.replaceState() を呼び出すことは、 popstate イベントのトリガーにはなりません。 popstate イベントは、戻るボタンをクリックしたり (又は JavaScript で history.back() を呼び出したり)、同じ文書で2つの履歴項目間を移動したりするように、ブラウザーのアクションを実行することのみがトリガーになります。

https://developer.mozilla.org/ja/docs/Web/API/WindowEventHandlers/onpopstate

フレームワークを利用する

上記のように生のjavascriptでtagを送信しようとするとプロダクトコードとPVトラッキング用のコードが密結合になりがちです。

Vue.jsなどのjavascriptフレームワークのRouterを使用している場合はルーティング後のアフターフックなど、ルーティングが変更された際のイベントをトリガーが用意されていることも多いです。

以下、Vue Routerの例ですが、router > index.js にて下記のようにafterEachを利用しタグを読み込むだけで上記要件のタイミングで発火することができます。

    router.afterEach(() => {
      gtag('config', 'UA-XXXXXXXXX-X');
    });`

https://router.vuejs.org/ja/guide/advanced/navigation-guards.html#グローバルな-after-フック

GoogleTagManagerを利用する

また、GoogleTagManager(以下GTM)を利用可能な場合にはGTMを利用すると、GTMのタグを埋め込んでおくと、発火タイミングはGTMの管理画面で設定できるためプロダクトコードとPVトラッキング用のコードをより疎結合にすることができます。

GTMでは「履歴の変更」というトリガーが用意されています。

https://support.google.com/tagmanager/answer/7679322?hl=ja

history API のhistory.pushstate()が使用されたとき、またURL の一部(ハッシュ)が変更されたときに発火されるため、上述のルーティングの方法に限らず要件2,3のタイミングで発火することができます。

まとめ

SPAでは、そもそもページビューの捉え方も人によって異なるので、目的に応じて定義する必要があります。
ルーティング時にPVを取得するのであれば、考慮しなければいけないことを少なくするという点で
GTM > フレームワーク > 生Javascript の優先度で検討するのが良さげではと思います。

そして最後に

HiCustomer ではエンジニアを募集しています!
少しでも興味をお持ちいただけた方、ぜひお気軽にオフィスに遊びにきてください。
弊社のVP of Engineeringの@hizeny まで、もしくは Wantedly でのご連絡をお待ちしています!