页面停留时间(TP)
页面停留时长同样借助effect
函数,通过计算页面变化的时间差从而上报页面停留时长事件,一般当进入第二个页面才会统计第一个页面的TP,进入三个页面计算第二个页面的TP。。。所以我们把逻辑写在getVisitor
函数中然后给它改个名
//上报uv&pv&TP const getVisitorAndTP = (app, prefix) => { const globalProperties = reactive(app.config.globalProperties); let startTime = new Date().getTime(); let path = ""; let lastPath = ""; effect(() => { const endTime = new Date().getTime(); const TP = endTime - startTime; startTime = endTime; lastPath = path; path = globalProperties.$route.path; //间隔为0不上报 if (!TP) return; console.log({ eventName: `${prefix}_${path}`, }); //页面停留时长小于0.5s不上报 if (TP < 500) return; console.log({ eventName: `${prefix}_${TP}_${lastPath}`, }); }); }; export default { install: (app, options) => { app.directive("click", { created: (el, bind) => { el.addEventListener("click", () => { console.log(bind.value); }); }, }); //挂载全局用于手动上报 app.config.globalProperties.$vtrack = (params) => { console.log(params) } } }
上传TP事件的格式为prefix_TP_path
,因此我们切换页面的时候可以看到同时上报的两个事件
获取公共参数
根据用户传来的固定参数baseParams
和事件前缀prefix
调整我们上报事件形式。假设在main.js
用户传来这些数据
import { createApp } from "vue"; import App from "./App.vue"; import router from "./router/index"; import vTracking from "v-tracking"; const app = createApp(App); app.use(router); app.use(vTracking, { baseParams: { uid: 123, userAgent: "Chrome", }, prefix: "app", }); app.mount("#app");
然后修改一下我们的插件(这里将uv/pv还有TP作为单独参数上报,不再使用上面的eventName
形式,太懒了,上面的写法不想改了😑)
import { reactive, effect } from "@vue/reactivity"; //上报uv&pv&TP const getVisitorAndTP = (app, prefix, baseParams) => { const globalProperties = reactive(app.config.globalProperties); let startTime = new Date().getTime(); let path = ""; let lastPath = ""; effect(() => { const endTime = new Date().getTime(); const TP = endTime - startTime; startTime = endTime; lastPath = path; path = globalProperties.$route.path; //间隔为0不上报 if (!TP) return; console.log({ ...baseParams, UPVEventName: `${prefix}_${path}`, }); //页面停留时长小于0.5s不上报 if (TP < 500) return; console.log({ ...baseParams, TP: { path: lastPath, time: TP, }, }); }); }; export default { install: (app, options) => { const { prefix, baseParams } = options; getVisitorAndTP(app, prefix || "track", baseParams || {}); app.directive("click", { created: (el, bind) => { el.addEventListener("click", () => { console.log({ ...bind.value, ...(baseParams || {}) }); }); }, }); //挂载全局用于手动上报 app.config.globalProperties.$vtrack = (params) => { console.log(params); }; }, };
此时这控制台打印出事件类型上报格式为
引入axios
最后简单写一个axios的请求函数,这里不考虑请求失败的情况,此时需要用户传入一个baseUrl
import { reactive, effect } from "@vue/reactivity"; import axios from "axios"; axios.defaults.headers["Content-Type"] = "application/json"; const request = (baseUrl, params) => { axios({ url: baseUrl, method: "post", data: params, }); }; //上报uv&pv&TP const getVisitorAndTP = (app, prefix, baseParams, baseUrl) => { const globalProperties = reactive(app.config.globalProperties); let startTime = new Date().getTime(); let path = ""; let lastPath = ""; effect(() => { const endTime = new Date().getTime(); const TP = endTime - startTime; startTime = endTime; lastPath = path; path = globalProperties.$route.path; //间隔为0不上报 if (!TP) return; request(baseUrl, { ...baseParams, UPVEventName: `${prefix}_${path}`, }); //页面停留时长小于0.5s不上报 if (TP < 500) return; request(baseUrl, { ...baseParams, TP: { path: lastPath, time: TP, }, }); }); }; export default { install: (app, options) => { const { prefix, baseParams,baseUrl } = options; getVisitorAndTP(app, prefix || "track", baseParams || {}, baseUrl); getVisitorAndTP(app, prefix || "track", baseParams || {}); app.directive("click", { created: (el, bind) => { el.addEventListener("click", () => { console.log({ ...bind.value, ...(baseParams || {}) }); }); }, }); //挂载全局用于手动上报 app.config.globalProperties.$vtrack = (params) => { console.log(params); }; }, };
此时便可以看到事件的请求了
打包发布
最后使用vite进行打包发布,全局安装vite
pnpm add vite -w -D
然后在v-tracking
下新建vite.config.js
,配置库模式打包cjs和es格式
import { defineConfig } from "vite"; import { resolve } from "path"; export default defineConfig({ build: { target: "modules", //压缩 minify: true, rollupOptions: { input: ["index.js"], //忽略文件 external: ["@vue/reactivity", "axios"], output: [ { format: "es", //不用打包成.es.js,这里我们想把它打包成.js entryFileNames: "[name].js", //配置打包根目录 dir: resolve(__dirname, "./dist/es"), }, { format: "cjs", //不用打包成.mjs entryFileNames: "[name].js", //配置打包根目录 dir: resolve(__dirname, "./dist/lib"), }, ], }, lib: { entry: "./index.js", name: "vtrack", }, }, });
然后将
v-tracking/package.json
入口文件指向打包后路径,其中module
代表如果项目支持es
格式的话就会使用dist/es/index.js
这个路径
{ "name": "v-tracking", "version": "1.0.0", "main": "dist/lib/index.js", "module": "dist/es/index.js", "description": "", "keywords": [], "files": [ "dist" ], "dependencies": { "@vue/reactivity": "^3.2.37", "axios": "^0.27.2" }, "author": "", "license": "MIT" }
最后在v-tracking目录下执行pnpm publish
进行发布(这里需要注册npm账户等等)
使用说明
安装
npm install v-tracking -S
在 main.js 中引入插件
import { createApp } from "vue"; import App from "./App.vue"; import router from "./router/index"; import vTracking from "v-tracking"; const app = createApp(App); app.use(router); app.use(vTracking, Options); app.mount("#app");
注意
因为涉及到路由检测,所以必须配合vue-router
使用
Options
- baseParams (string)
公共参数,每次上报都会携带的参数,比如用户的登录信息 uid 等
- baseUrl (string)
上报的后台请求地址,后端接口需按照前端请求参数设计
- prefix (string)
PV&UV&TP 事件前缀,一般用于区分不同项目等(建议和普通事件前缀一致)
- isVisTP (Boolean)
是否统计页面 UV&PV&PT
Options 示例
app.use(vTracking, { baseParams: { uid: 123 }, baseUrl: "http://example/event", prefix: "app", isVisTP: false, });
点击指令上报
<template> <div>page1</div> <div v-click="{ eventName: 'test1' }">click</div> </template>
后台接收数据格式为
{ uid: 123 , eventName: "test1" }
手动上报
<template> <div>page1</div> <div @click="track">click</div> </template> <script setup> import { getCurrentInstance } from 'vue'; const { proxy } = getCurrentInstance() //手动上报事件 const track = ()=>{ proxy.$vtrack({ eventName: 'test1' }) } </script>
后台接收数据格式为
{ uid: 123, eventName: "test1" }
UV&PV
isVisTP
为 true 时候插件会自动上报每个页面进入时的数据,其中后台接收数据格式为
{ uid: 123, UPVEventName: `${prefix}_${path}` }
其中
path
为页面路由路径,如/page1
页面停留时长(TP)
isVisTP
为 true 时候插件会自动上报每个页面用户停留时长,其中后台接收数据格式为
{ uid: 123, TP: { path: "/page2", time: 1269446 }, }
time 则表示时长(ms)
写在最后
本篇文章旨在提供一些思路,难免会有不妥或者错误之处,也欢迎大家评论区指出不胜感激。仓库地址vue-utils