これは はてなエンジニア Advent Calendar 2019 14日目の記事です。
昨日は id:takuji31 さんの Kotlin Coroutinesをテストする - Takuji->find; でした。
最近は仕事で Next.js を使っているのですが、その中で Google Analytics による計測をどうするかという話題があったので紹介します。
前提
- Next.js ((+ TypeScript) + Apollo Client) の話です
- SPA 的遷移は使って無くて、都度 SSR で遷移してる
- 僕は Google Analytics とその実装にまあまあ詳しい自信があります
- 一方で React を完全に理解しているかというとそんな自信はない
tl;dr
- next/head の <Head> 内で gtag.js をロードすることにしました
- イベントの計測は、その子要素内で起きた特定の種類のイベントを拾う、というようなコンポーネントをいくつか自作してそれを使っています
- next-plugin-google-analytics というのが開発されているようでウォッチしてます
- というかプラグインの仕組み自体から検討が進んでいる
- 最強の結論とは思っていなくて、落とし所という雰囲気です
自転車置き場の議論っぽい気もしますが、このような選択に至った理由について書いていきます。いろいろ悩みながらの判断という感じだったので、よりよい方法やご意見などあればぜひ教えていただけると嬉しいです!!!
そもそもどうやって Google Analytics を設置するか
Web ページに Google Analytics を導入する方法は数多存在するわけですが、ふつうはこれらの選択肢の中から選ぶことになると思います:
- いわゆる普通の設置タグ (gtag.js や analytics.js) をコピペしてくる
- もっとも無難と思われる
- Google タグマネージャー (GTM) を設置し、GTM の設定で Google Analytics を呼び出す
- これも無難
- 利用しているフレームワークなどに合わせて、gtag.js や analytics.js の設置を肩代わりしてくれるライブラリを使う
- react-ga とか
- 自分で Measurement Protocol を叩く、もしくはこれをラップしたライブラリなどを使う
- ハードコアだと思う……
gtag.js・analytics.js・GTM のどれかを利用する方法がもっともメジャーかつ支配的だと思いますが、これらのタグはだいたい自分でなんらかの状態を持っていたり、提供されている読み込み用コードの実行に副作用を持つものもあることに注意する必要があります。
たとえば、gtag.js のヘルプには以下のような記述があります:
ページビュー測定を無効化するこのコードのデフォルトの動作は、Google アナリティクスにページビュー ヒットを送信することです。ほとんどの場合はこの動作で問題ありません。サイトの各ページにコードを追加すれば、以降は自動的にページビューが測定されます。なんらかの理由で、Google アナリティクスにページビュー ヒットが送信されないようにしたい場合は、
send_page_view
パラメータをfalse
に設定します。gtag('config', 'GA_MEASUREMENT_ID', { 'send_page_view': false });
サイトに gtag.js を追加する | ウェブ向けアナリティクス(gtag.js) | Google Developers
つまるところ、gtag.js をロードしている <script>
タグが不意に再レンダリングされないようする必要がある、という話なのですが、これは next/head
パッケージで提供されている <Head>
コンポーネントを利用することで回避できます *1。具体的にはこういう例がよく示されています:
<Head> <script async src={`https://www.googletagmanager.com/gtag/js?id=UA-XXXXXX-X`} /> <script dangerouslySetInnerHTML={{ __html: ` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments)}; gtag('js', new Date()); gtag('config', 'UA-XXXXXX-X'); `, }} /> </Head>
"Next.js Google Analytics" とかで検索した人がたいてい見るであろう zeit/next.js の issue #160 などでもこれが紹介されていますし、前述の next-plugin-google-analytics も今の時点ではこの方式を使おうとしているようです。
Adding GA script tag? · Issue #160 · zeit/next.js · GitHub
next.js/document-head-tags-server.js at dc51a705c1591592510a082a7be00ccf1c5df295 · zeit/next.js · GitHub
というわけで、そういうの ☝️を _app.js に書くのが一番良さそう、というのがいまのところの結論です。念の為 key
prop を設定してもよいかもしれません。
<Link>
によるクライアントサイドでの遷移のページビュー
これはあんまりわかっていなくて *2、というのは
- Router から
routeChangeComplete(url)
イベントというのが取れるのでそれを使えるはずRouter Events (Documentation - Getting Started | Next.js)
- このイベントハンドラの中で、ページビューの計測を発火したら良いはず
- gtag.js だと
gtag('config', ...)
- gtag.js だと
- 一方で、
routeChangeComplete
されたタイミングではまだページのコンテンツを組み立て中ということがある- 具体的には、何らかの API からコンテンツを取得している最中だったりとか
- 遷移後の URL はわかっているものの、title 要素が意図したものに置き換わってなかったりするので、遷移後の URL + 遷移前の title のページビューとして Google Analytics に送信されてしまう
- Google Analytics で分析をするときに、ページタイトルを軸にして集計することはたまにあるはずなので、これでは困りそう
- 何を持ってページビュー計測準備完了かというのはそのページによって異なるはずなので、外側から何かを観測してフックしたりするのは不可能な印象がある、ページのコンポーネントそれぞれでなんかやる必要がある?
どうするのがいいんでしょうかね、ページのコンポーネント
イベント計測
ここでこういうイベントを拾う、というのを React 世界でどう表現するか
どういうイベントを計測したいか、というのも場合によりけりですが、要素のクリックや視認 (imp) などを拾えるようなグッズを準備できれば大抵の場合は間に合うものと思います。で、実際にページを記述するコンポーネントにグッズを仕込んでいくわけですが、それにもいくつかのやり方があると思います:
- onClick などイベントハンドラに仕込む用のメソッドを用意して、計測したいポイントでそれを呼ぶことにする
- Cons: 計測に関する話題までそのコンポーネントの責務になってしまうことで話が難しくなる (個人の感想)
- イベントバブリングを利用する想定で、計測対象のコンポーネントを囲むような形で設置するコンポーネントを用意する
<ClickTracker eventName="foobar"><Foobar /></ClickTracker>
みたいな形で使う- Pros: 1. 案と比べて、コンポーネントごとに一つの機能を果たす世界観に近い
- Cons: 内側で
preventDefault
などされるとイベント計測は発火しなくなる - Cons:
div
などが必要になるので、そのぶん HTML 要素のネストが深くなってしまう
- 計測対象のコンポーネントをくるむ HOC を用意する
- Pros: 計測対象の要素にそのまま onClick なりを設定するようにすれば、2. 案と比べて余分に HTML のネストが深くなるということはない
- Cons: HOC で onClick なりを触りたくない…… (個人の感想)
- イベントバブリングに乗っかるのとどっちがいいのか、という話
data-
属性などにイベントの情報を含めて、その属性を持つ要素に対してクリックイベントを設定する、というふうにする
どの方式が一番適しているのかも場合によりけりなのではないかとは思っているのですが、今回はチームでの議論を経て 2. 案を選びました。
イベントを送る
実際にイベントを送信する際には、自前の軽いラッパーを通して gtag('event', ...)
を直接叩く方法を選びました。
Google アナリティクスのイベントを測定する | ウェブ向けアナリティクス(gtag.js) | Google Developers
gtag()
は引数にいろいろ指定することでいろいろできる、という感じの API なのですが、TypeScript を使っているのであれば DefinitelyTyped の @types/gtag.js
パッケージを使うことで単純な引数のミスなんかは減らせると思います。
たびたび言及している react-ga などを使わないことにしたのは、この程度であればライブラリに頼るほどでもないかなと思ったのが理由です。
おわり
ということで、Next.js に Google Analytics による計測を実装していく中で考えたことをご紹介しました。2020 年も next-plugin-google-analytics 元年としてやっていきましょう。
はてなエンジニア Advent Calendar 2019 明日の担当は
id:susisu さんです。フロントエンドネタが続きそうな気がする!