返回

Nuxt2 嵌套路由下 vue-gtag 重复上报 GA4 页面浏览事件解决方案

vue.js

Nuxt 2嵌套路由与vue-gtag重复页面浏览追踪问题

使用 Nuxt 2 开发项目时,配置自定义路由并结合 vue-gtag 实现 Google Analytics 4 (GA4) 追踪是很常见的需求。然而,在某些情况下,尤其是使用嵌套路由时,可能会出现重复发送页面浏览事件的问题。具体表现为:访问父路由(如 NewsList)时触发一个页面浏览事件,而访问嵌套子路由(如 NewsDetail)时却触发两个页面浏览事件,导致数据统计不准确。

问题分析

造成此问题的原因在于 vue-gtag 的工作方式以及 Nuxt 2 的路由处理机制。vue-gtag 默认监听 Vue Router 的 afterEach 钩子来发送页面浏览事件。当路由发生变化时,vue-gtag 会认为这是一次新的页面访问并发送事件。然而,在嵌套路由场景中,由于组件的渲染过程会多次触发 afterEach 钩子,导致重复发送页面浏览事件,特别是在嵌套比较深的情况下更为明显。对于我们示例代码中的/news/:tag?/:id([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})/:title这种带有动态id和title的嵌套路由,由于id值是动态变化的,组件和vue-gtag会更加频繁的检测路由的变化,从而导致两次页面浏览的事件上报。

解决方案

以下是一些避免重复发送页面浏览事件的方案,您可以根据自身情况选择合适的方案。

1. 手动控制 vue-gtag 页面浏览事件发送

默认情况下 vue-gtag 会监听路由变化自动发送事件。 可以通过配置 vue-gtag 禁用此行为,然后手动在路由切换完成时发送事件。这种方法提供了更高的控制粒度,能有效地解决嵌套路由的问题。

实现步骤:

  1. 禁用自动页面跟踪 : 在 plugins/vue-gtag-client.js 中,修改 vue-gtag 的配置,设置 bootstraptrue, 并且移除第二个参数 app.router,这样vue-gtag就不会在Vue router变化时,自动上报。

    import Vue from 'vue'
    import VueGtag from 'vue-gtag'
    
    const vueGtag = ({ app, $config: { GTAG_MEASUREMENT_ID } }, inject) => {
        Vue.use(
          VueGtag,
          {
            appName: 'ch_ui',
            bootstrap: true, // 禁用自动页面追踪
            config: { id: GTAG_MEASUREMENT_ID },
            deferScriptLoad: true,
            enabled: false,
            onError(error) {
              console.error('script failed to load', error)
            },
            pageTrackerTemplate(to) {
             return {
               page_title: to.name,
               page_path: to.fullPath,
             }
           },
            params: {
              anonymize_ip: true,
            },
          },
        )
        inject('gtag', Vue.$gtag)
      }
    
      export default vueGtag
    
  2. nuxt.config.js 文件添加一个监听router变化的hook ,并在该hook中上报页面浏览事件:

export default {
  router: {
  middleware:['gtag'], // 可以考虑使用一个统一的中间件处理。
    extendRoutes(routes, resolve) {

        },
  },

 plugins: [
      '~/plugins/axios.js',
      '~/plugins/buefy.js',
      '~/plugins/fontawesome.js',
      '~/plugins/utils.js',
      '~/plugins/vue-gtag.client.js',
      '~/plugins/vue-gtag.server.js',
      '~/plugins/vanilla-cookieconsent.client.js',
      '~/plugins/vue-native-websocket.client.js',
  ],
  // ...其他配置
}
  1. 新建一个 middleware

middlewares/gtag.js

```javascript
export default function ({ route, $gtag}) {
  $gtag.pageview({
  page_title: route.name,
   page_path: route.fullPath,
  })

}
```

这种方式能够很好地解决由于自动触发路由导致事件上报重复的问题。同时我们可以基于路由设置更精细化的上报策略。

2. 利用 Nuxt.js 的 beforeNuxtRender Hook

另一种方法是使用 Nuxt.js 提供的 beforeNuxtRender 生命周期钩子。这个钩子会在每次页面渲染之前被触发,可以用来发送 GA4 的页面浏览事件。这种方式虽然也属于手动控制的范围,但是执行顺序是在nuxt 页面真正开始渲染之前,也是可以很好的规避vue-gtag由于vue生命周期以及nuxt渲染机制导致的页面重复上报的事件。

实现步骤:

  1. 禁用自动页面跟踪 : 与方法 1 相同, 在 plugins/vue-gtag-client.js 中禁用自动页面跟踪,即设置 bootstrap: true, 并去除参数app.router

  2. 修改 nuxt.config.js 配置:

    export default {
      // ...其他配置
       hooks: {
          render: {
             before: async (context) => {
                   context.beforeNuxtRender((params)=>{
                         params.nuxtState.gtagPageView=
                         {
                           page_title: params.route.name,
                           page_path: params.route.fullPath,
                          }
                 })
             },
          },
       },
    
     plugins: [
          '~/plugins/axios.js',
          '~/plugins/buefy.js',
          '~/plugins/fontawesome.js',
         '~/plugins/utils.js',
          '~/plugins/vue-gtag.client.js',
           '~/plugins/vue-gtag.server.js',
           '~/plugins/vanilla-cookieconsent.client.js',
          '~/plugins/vue-native-websocket.client.js',
     ],
    }
    
  3. plugins/vue-gtag-client.js 获取gtagPageView变量并在钩子中触发GA4的上报 :

    import Vue from 'vue'
    import VueGtag from 'vue-gtag'
    
    const vueGtag = ({ app, $config: { GTAG_MEASUREMENT_ID } }, inject) => {
        Vue.use(
          VueGtag,
          {
            appName: 'ch_ui',
            bootstrap: true, // 禁用自动页面追踪
            config: { id: GTAG_MEASUREMENT_ID },
            deferScriptLoad: true,
            enabled: false,
            onError(error) {
              console.error('script failed to load', error)
            },
    
            params: {
              anonymize_ip: true,
            },
          },
        )
    
        // 这里监听 Nuxt 的 beforeNuxtRender 生命周期钩子来发送 GA4 事件
      app.router.afterEach((to, from) => {
         if (to.fullPath) { // to 是 next 路由对象,从 to.fullPath 读取完整路径
           // 如果 `to` 路径被 `beforeNuxtRender` 事件更改过了则直接发送
          if(app.nuxtState && app.nuxtState.gtagPageView ){
             Vue.$gtag.pageview(app.nuxtState.gtagPageView)
               delete  app.nuxtState.gtagPageView
          } else {
           Vue.$gtag.pageview({
             page_title: to.name,
             page_path: to.fullPath,
           })
          }
       }
    
      })
    
        inject('gtag', Vue.$gtag)
      }
    
      export default vueGtag
    

此方案的好处是它可以在页面实际渲染之前发送页面浏览事件,从而确保数据的准确性,也可以在beforeNuxtRender中添加更复杂和灵活的逻辑处理。同时解决了vue-gtag自动上报导致的页面重复上报事件。

注意事项

  • 数据验证: 在实施任何解决方案后,都需要通过 GA4 的实时报告或相关监控工具验证数据是否准确,确保重复页面浏览事件已解决,并且统计信息符合预期。
  • 错误处理: onError 配置用于处理脚本加载失败等情况,应注意在错误发生时记录并排查问题。

以上两个方案能比较有效地解决vue-gtag 在处理嵌套路由时重复触发页面浏览的问题,开发者可基于自身项目特性灵活选择。