前端埋點對于那些營銷活動的專案是必須的,它可以反應出用戶的喜好與習慣,從而讓專案的運營者們能夠調整策略優化流程提高用戶體驗從而獲取更多的$,這篇文章將實作一個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形式,太懶了,上面的寫法不想改了??)
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", (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);
app.directive("click", (el, bind) => {
el.addEventListener("click", () => {
request(baseUrl, { ...bind.value, ...(baseParams || {}) });
});
});
//掛載全域用于手動上報
app.config.globalProperties.$vtrack = (params) => {
request(baseUrl, { ...params, ...(baseParams || {}) });
};
},
};
此時便可以看到事件的請求了
打包發布
最后使用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
都看到這了,點個贊再走吧~??
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/503130.html
標籤:其他
上一篇:第一章、權衡的藝術
