前端埋點對於那些營銷活動的項目是必須的,它可以反應出用戶的喜好與習慣,從而讓項目的運營者們能夠調整策略優化流程提高用戶體驗從而獲取更多的$。這篇文章將實現一個Vue3版本的埋點上報插件,主要功能有 通過Vue自定義指令形式實現點擊事件上報 提供手動調用上報方法 上報每個頁面訪問人數與次數(UV,PV ...
前端埋點對於那些營銷活動的項目是必須的,它可以反應出用戶的喜好與習慣,從而讓項目的運營者們能夠調整策略優化流程提高用戶體驗從而獲取更多的$。這篇文章將實現一個Vue3版本的埋點上報插件,主要功能有
- 通過Vue自定義指令形式實現點擊事件上報
- 提供手動調用上報方法
- 上報每個頁面訪問人數與次數(UV,PV)
- 上報用戶在每個頁面停留時長
項目環境搭建
本項目採用pnpm進行Monorepo環境搭建,因為未來這個項目可能會加入更多的工具包.
安裝pnpm
npm install pnpm -g
初始化package.json
pnpm init
新建配置文件 .npmrc
shamefully-hoist = true
新建pnpm-workspace.yaml
packages:
- "packages/**"
- "play"
此時我們的packages
目錄和play
目錄便關聯起來的,我們後面就可以愉快的在本地調試了。其中packages
是我們各種包存放的地方,具體我們本次開發的埋點插件v-tracking
便是其中之一。play則是一個Vue3項目用來測試我們的本地包,它的創建方法這裡就不再詳細說了。最終它的目錄結構如下
插件開發
終端進入v-tracking
,執行pnpm init
讓它成為一個包,然後新建index.js
作為入口。
在vue3是通過 app.use(plugin)
的形式引入插件的,它會直接調用插件的install
方法.install
會接收到應用實例和傳遞給 app.use()
的額外選項作為參數。所以我們在v-tracking/index.js
預設導出一個帶有install
函數的對象
export default {
install: (app, options) => {
console.log(options)
}
}
進入paly
執行pnpm add v-tracking
此時你會發現paly
下的package.json
多了個這樣的依賴
這樣就是表示play
已經關聯到本地的包[email protected]
的包了,然後我們在paly
的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
}
})
app.mount('#app')
啟動項目我們會發現install
函數被調用了,並且獲取到了傳來的額外參數.
點擊事件上報
點擊事件的上報我們提供兩種方式,一種是以Vue自定義指令的形式,一種是手動調用上報方法。因為指令形式的點擊上報並不能實現非同步上報,所以加入手動調用上報的方法
vue自定義指令
首先我們簡單瞭解一下什麼是自定義指令。我們都用過Vue的內置的一系列指令 (比如 v-model
或 v-show
) 等,而Vue還提供了註冊自定義指令的函數directive
用法如下,其中el
是我們綁定指令的dom,binding
則是指令傳來的一系列參數,比如
<div v-example:foo.bar="baz">
binding則是這樣一個對象
{
arg: 'foo',
modifiers: { bar: true },
value: /* `baz` 的值 */,
oldValue: /* 上一次更新時 `baz` 的值 */
}
瞭解完指令我們便可以開始自定義指令click
的開發了。其實很簡單,就是監聽el
的點擊事件然後獲取到指令的value
上報給後端即可
export default {
install: (app, options) => {
app.directive('click', (el, bind) => {
el.addEventListener('click', () => {
console.log(bind.value)
})
})
}
}
我們在play
的page1.vue
種進行綁定指令測試
<template>
<div v-click="{ eventName: 'test1' }">test1</div>
</template>
我們點擊test1
便可以在控制台看到我們需要上報的數據
手動上報方法
我們可以手動調用上報方法掛載在實例全局即可,在vue3種掛載全局屬性的方法是app.config.globalProperties.xxx
,所以我們定義一個全局上報方法$vtrack
export default {
install: (app, options) => {
app.directive('click', (el, bind) => {
el.addEventListener('click', () => {
console.log(bind.value)
})
})
//掛載全局用於手動上報
app.config.globalProperties.$vtrack = (params) => {
console.log(params)
}
}
}
然後我們在page1.vue
中進行使用
<template>
<div v-click="{ eventName: 'test1' }">test1</div>
</template>
<script setup>
import { getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance()
proxy.$vtrack({ eventName: 'test1' })
</script>
同樣的我們可以獲取到我們需要的上報數據。
頁面訪問次數上報(pv,uv)
對於頁面訪問次數或者人數我們可以通過檢測路由的變化從而上報當前頁面事件。比如在page1
頁面我們可以以prefix_/page1
(這個首碼可以由自己來定義)形式上報。但是在插件中如何檢測路由變化呢?
起初我想通過監聽onhashchange
事件來監聽路由變化的,但是經過測試發現Vue中的push事件根本不會觸發onhashchange
。所以我便引入了@vue/reactivity
,通過它的reactive
讓傳入app
實例進行一個響應式包裹,再通過effect
函數監聽路由變化從而實現統計每個頁面的進入事件,首先安裝
pnpm add @vue/reactivity -w
然後引用
import { reactive,effect } from '@vue/reactivity'
//uv and pv
const getVisitor = (app, prefix) => {
const globalProperties = reactive(app.config.globalProperties);
effect(() => {
const path = globalProperties.$route.path;
console.log({
eventName: `${prefix}_${path}`,
});
});
};
export default {
install: (app, options) => {
stayTime();
getVisitor(app, "track");
app.directive("click", (el, bind) => {
el.addEventListener("click", () => {
console.log(bind.value);
});
});
//掛載全局用於手動上報
app.config.globalProperties.$vtrack = (params) => {
console.log(params);
};
},
};
然後在項目中切換路由就會獲取到需要上報的事件
頁面停留時間(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) => {
getVisitorAndTP(app, "track");
app.directive("click", (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
形式,太懶了,上面的寫法不想改了