Browse Source

feature:google analytics数据打点demo

cmy 4 weeks ago
parent
commit
cb64ee24f7

+ 21 - 4
main.html

@@ -1,5 +1,6 @@
 <!DOCTYPE html>
 <html lang="en">
+
 <head>
   <meta charset="UTF-8">
   <title>Document</title>
@@ -7,11 +8,27 @@
 
 <body>
   这是一个空页面
-  <button id="btn">点击我</button>
+  <button id="btn" onclick="handleClick()">点击我</button>
 </body>
-
+<!-- Google tag (gtag.js) -->
+<script async src="https://www.googletagmanager.com/gtag/js?id=G-8HEB2QHV86"></script>
 <script>
-    !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
-    posthog.init('phc_5YuF937Fs3N0djI4XFThsGAZfgaciU9pjKw6T3SQjvV',{api_host:'https://us.i.posthog.com', defaults:'2025-05-24'})
+  window.dataLayer = window.dataLayer || [];
+  function gtag(){dataLayer.push(arguments);}
+  gtag('js', new Date());
+  gtag('config', 'G-8HEB2QHV86');
+  gtag('event', 'pageview', {
+    page_path: '/',
+    value: "js:访问页面"
+  });
+
+  function handleClick() {
+    gtag('event', 'click', {
+      event_category: 'test',
+      event_label: 'click',
+      value: 'js:点击按钮'
+    });
+  }
 </script>
+
 </html>

+ 7 - 0
react-test/package-lock.json

@@ -15,6 +15,7 @@
         "posthog-js": "^1.256.1",
         "react": "^19.1.0",
         "react-dom": "^19.1.0",
+        "react-ga4": "^2.1.0",
         "react-scripts": "5.0.1",
         "web-vitals": "^2.1.4"
       }
@@ -13955,6 +13956,12 @@
       "integrity": "sha512-SN/U6Ytxf1QGkw/9ve5Y+NxBbZM6Ht95tuXNMKs8EJyFa/Vy/+Co3stop3KBHARfn/giv+Lj1uUnTfOJ3moFEQ==",
       "license": "MIT"
     },
+    "node_modules/react-ga4": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/react-ga4/-/react-ga4-2.1.0.tgz",
+      "integrity": "sha512-ZKS7PGNFqqMd3PJ6+C2Jtz/o1iU9ggiy8Y8nUeksgVuvNISbmrQtJiZNvC/TjDsqD0QlU5Wkgs7i+w9+OjHhhQ==",
+      "license": "MIT"
+    },
     "node_modules/react-is": {
       "version": "17.0.2",
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",

+ 1 - 0
react-test/package.json

@@ -10,6 +10,7 @@
     "posthog-js": "^1.256.1",
     "react": "^19.1.0",
     "react-dom": "^19.1.0",
+    "react-ga4": "^2.1.0",
     "react-scripts": "5.0.1",
     "web-vitals": "^2.1.4"
   },

+ 27 - 3
react-test/src/App.js

@@ -1,11 +1,35 @@
-import logo from './logo.svg';
+import React, { useEffect } from 'react';
+import ReactGA from 'react-ga4';
 import './App.css';
 
 function App() {
+  useEffect(() => {
+    ReactGA.initialize('G-8HEB2QHV86');
+    ReactGA.send('js', new Date());
+    ReactGA.send('pageview', { 
+      page_path: '/',
+      value: "react:访问页面"
+     });
+  }, []);
+
+  /**
+   * 点击按钮
+   */
+  const handleClick = () => {
+    ReactGA.event({
+      category: 'test',
+      action: 'click',
+      label: 'react:点击按钮'
+    });
+  }
+
   return (
     <div className="App">
-      这是个空白页
-      <button>按钮</button>
+      <button
+        onClick={handleClick}
+      >
+        按钮
+      </button>
     </div>
   );
 }

+ 33 - 1
vue-test/package-lock.json

@@ -10,7 +10,9 @@
       "dependencies": {
         "core-js": "^3.8.3",
         "posthog-js": "^1.256.1",
-        "vue": "^3.2.13"
+        "vue": "^3.2.13",
+        "vue-gtag-next": "^1.14.0",
+        "vue-router": "^4.5.1"
       },
       "devDependencies": {
         "@babel/core": "^7.12.16",
@@ -2901,6 +2903,12 @@
         "url": "https://opencollective.com/postcss/"
       }
     },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "license": "MIT"
+    },
     "node_modules/@vue/reactivity": {
       "version": "3.5.17",
       "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.17.tgz",
@@ -11002,6 +11010,15 @@
         "node": ">=10"
       }
     },
+    "node_modules/vue-gtag-next": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmjs.org/vue-gtag-next/-/vue-gtag-next-1.14.0.tgz",
+      "integrity": "sha512-iJl+cOG2GU5NuxqzSSIpt03WVOvZqyKB9TOy7d55KiuvRklcnb2nlqxW5B/a3/sbIt7fla+XEkRyMCcoz0zAHw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "vue": "^3.0.0-rc.11"
+      }
+    },
     "node_modules/vue-hot-reload-api": {
       "version": "2.3.4",
       "resolved": "https://registry.npmmirror.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
@@ -11039,6 +11056,21 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/vue-router": {
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz",
+      "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.6.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
     "node_modules/vue-style-loader": {
       "version": "4.1.3",
       "resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz",

+ 3 - 1
vue-test/package.json

@@ -10,7 +10,9 @@
   "dependencies": {
     "core-js": "^3.8.3",
     "posthog-js": "^1.256.1",
-    "vue": "^3.2.13"
+    "vue": "^3.2.13",
+    "vue-gtag-next": "^1.14.0",
+    "vue-router": "^4.5.1"
   },
   "devDependencies": {
     "@babel/core": "^7.12.16",

+ 64 - 8
vue-test/src/App.vue

@@ -1,17 +1,73 @@
 <template>
-  <img alt="Vue logo" src="./assets/logo.png">
-  <HelloWorld msg="Welcome to Your Vue.js App"/>
+  <div @click="handleClick">点击我</div>
 </template>
 
-<script>
-import HelloWorld from './components/HelloWorld.vue'
+<script setup>
+import { onMounted, onUnmounted } from 'vue'
+import { useGtag } from 'vue-gtag-next'
+const gtag = useGtag()
 
-export default {
-  name: 'App',
-  components: {
-    HelloWorld
+const safeGtagEvent = (name, params) => {
+  if (gtag) {
+    gtag.event(name, params)
+  } else {
+    console.warn('gtag 未初始化')
   }
 }
+
+// 页面访问
+safeGtagEvent('pageview', { 
+  page_path: '/',
+  value: "vue:访问页面"
+})
+
+const handleClick = () => {
+  safeGtagEvent('click', {
+    event_category: 'test',
+    event_label: 'vue:点击按钮',
+    value: 1
+  })
+}
+
+const handleUserExit = () => {
+  safeGtagEvent('user_exit', {
+    event_category: 'engagement',
+    event_label: 'vue:页面直接退出'
+  })
+}
+
+onMounted(() => {
+  safeGtagEvent('demo_loaded', {
+    event_category: 'load',
+    event_label: 'vue:加载完成'
+  })
+
+  if (document.visibilityState === 'visible') {
+    safeGtagEvent('tab_visible', {
+      event_category: 'tab',
+      event_label: 'vue:切回本标签页',
+      value: {
+        a: 1,
+        b: 2
+      }
+    })
+  }
+
+  const referrer = document.referrer
+  const urlParams = new URLSearchParams(window.location.search)
+  const utmSource = urlParams.get('utm_source')
+  safeGtagEvent('user_source', {
+    event_category: 'acquisition',
+    event_label: referrer || utmSource || '直接访问',
+    value: "vue:用户来源"
+  })
+
+  window.addEventListener('beforeunload', handleUserExit, { once: true })
+})
+
+onUnmounted(() => {
+  window.removeEventListener('beforeunload', handleUserExit)
+})
 </script>
 
 <style>

+ 9 - 6
vue-test/src/main.js

@@ -1,9 +1,12 @@
+// main.js
 import { createApp } from 'vue'
 import App from './App.vue'
-import posthog from "posthog-js";
+import VueGtag from 'vue-gtag-next'
+import router from './router'
 
-posthog.init("phc_5YuF937Fs3N0djI4XFThsGAZfgaciU9pjKw6T3SQjvV", {
-  api_host: "https://us.i.posthog.com",
-  defaults: '2025-05-24',
-});
-createApp(App).provide("posthog", posthog).mount('#app')
+const app = createApp(App)
+app.use(VueGtag, {
+  property: { id: 'G-8HEB2QHV86' }
+})
+app.use(router)
+app.mount('#app')

+ 22 - 0
vue-test/src/router.js

@@ -0,0 +1,22 @@
+import { createRouter, createWebHistory } from 'vue-router'
+import { useGtag } from 'vue-gtag-next'
+const gtag = useGtag()
+
+const router = createRouter({
+    history: createWebHistory(),
+    routes: [
+        
+    ]
+})
+
+router.beforeEach((to, from, next) => {
+    if (from.fullPath !== '/') { // 避免首次进入也触发
+        // 访问结束后跳转其他页面
+        gtag.event('page_leave', {
+            event_category: 'navigation',
+            event_label: `from: ${from.fullPath} to: ${to.fullPath}`
+        });
+    }
+    next();
+});
+export default router;

+ 18 - 1
vue-test/yarn.lock

@@ -1547,6 +1547,11 @@
   optionalDependencies:
     prettier "^1.18.2 || ^2.0.0"
 
+"@vue/devtools-api@^6.6.4":
+  version "6.6.4"
+  resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz"
+  integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==
+
 "@vue/[email protected]":
   version "3.5.17"
   resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.17.tgz"
@@ -5932,6 +5937,11 @@ vue-eslint-parser@^8.0.1:
     lodash "^4.17.21"
     semver "^7.3.5"
 
+vue-gtag-next@^1.14.0:
+  version "1.14.0"
+  resolved "https://registry.npmjs.org/vue-gtag-next/-/vue-gtag-next-1.14.0.tgz"
+  integrity sha512-iJl+cOG2GU5NuxqzSSIpt03WVOvZqyKB9TOy7d55KiuvRklcnb2nlqxW5B/a3/sbIt7fla+XEkRyMCcoz0zAHw==
+
 vue-hot-reload-api@^2.3.0:
   version "2.3.4"
   resolved "https://registry.npmmirror.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz"
@@ -5946,6 +5956,13 @@ vue-loader@^17.0.0:
     hash-sum "^2.0.0"
     watchpack "^2.4.0"
 
+vue-router@^4.5.1:
+  version "4.5.1"
+  resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz"
+  integrity sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==
+  dependencies:
+    "@vue/devtools-api" "^6.6.4"
+
 vue-style-loader@^4.1.0, vue-style-loader@^4.1.3:
   version "4.1.3"
   resolved "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz"
@@ -5959,7 +5976,7 @@ vue-template-es2015-compiler@^1.9.0:
   resolved "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz"
   integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
 
-vue@*, "vue@^2 || ^3.2.13", vue@^3.2.13, [email protected]:
+vue@*, "vue@^2 || ^3.2.13", vue@^3.0.0-rc.11, vue@^3.2.0, vue@^3.2.13, [email protected]:
   version "3.5.17"
   resolved "https://registry.npmmirror.com/vue/-/vue-3.5.17.tgz"
   integrity sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==