基于Vue+Node.js實(shí)現(xiàn)埋點(diǎn)功能全流程

要實(shí)現(xiàn)用戶訪問量統(tǒng)計(jì)和模塊點(diǎn)擊統(tǒng)計(jì)功能,可以采用以下方案:
一、前端(Vue)實(shí)現(xiàn)
1. 安裝必要的依賴
npm install axios --save # 用于發(fā)送請求 npm install js-cookie --save # 用于識別用戶
2. 創(chuàng)建埋點(diǎn)工具類 (tracker.js)
import axios from 'axios';
import Cookies from 'js-cookie';
const Tracker = {
// 初始化用戶ID(如果不存在)
initUserId() {
let userId = Cookies.get('user_id');
if (!userId) {
userId = 'user_' + Math.random().toString(36).substr(2, 9);
Cookies.set('user_id', userId, { expires: 365 });
}
return userId;
},
// 發(fā)送頁面訪問埋點(diǎn)
trackPageView() {
const userId = this.initUserId();
const data = {
userId: userId,
eventType: 'pageview',
pageUrl: window.location.href,
timestamp: new Date().toISOString()
};
this.sendData(data);
},
// 發(fā)送模塊點(diǎn)擊埋點(diǎn)
trackModuleClick(moduleName) {
const userId = this.initUserId();
const data = {
userId: userId,
eventType: 'module_click',
moduleName: moduleName,
timestamp: new Date().toISOString()
};
this.sendData(data);
},
// 發(fā)送數(shù)據(jù)到后端
sendData(data) {
axios.post('/api/track', data)
.catch(error => {
console.error('Tracking error:', error);
});
}
};
export default Tracker;
3. 在Vue應(yīng)用中使用
全局埋點(diǎn) (main.js)
import Tracker from './utils/tracker';
import router from './router';
// 頁面訪問埋點(diǎn)
router.afterEach((to, from) => {
Tracker.trackPageView();
});
// 掛載到Vue原型,方便組件內(nèi)使用
Vue.prototype.$tracker = Tracker;
組件內(nèi)模塊點(diǎn)擊埋點(diǎn)
<template>
<div>
<button @click="handleClick('module1')">模塊1</button>
<button @click="handleClick('module2')">模塊2</button>
</div>
</template>
<script>
export default {
methods: {
handleClick(moduleName) {
// 業(yè)務(wù)邏輯...
// 埋點(diǎn)
this.$tracker.trackModuleClick(moduleName);
}
}
}
</script>
二、后端(Node.js)實(shí)現(xiàn)
1. 創(chuàng)建數(shù)據(jù)庫表
假設(shè)使用MySQL:
CREATE TABLE tracking_events ( id INT AUTO_INCREMENT PRIMARY KEY, user_id VARCHAR(255), event_type VARCHAR(50), page_url VARCHAR(500), module_name VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE daily_stats ( id INT AUTO_INCREMENT PRIMARY KEY, date DATE UNIQUE, total_visits INT DEFAULT 0, unique_visitors INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); CREATE TABLE module_stats ( id INT AUTO_INCREMENT PRIMARY KEY, module_name VARCHAR(100) UNIQUE, click_count INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );
2. Node.js 后端接口 (Express示例)
const express = require('express');
const bodyParser = require('body-parser');
const mysql = require('mysql2/promise');
const app = express();
// 數(shù)據(jù)庫連接配置
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'tracking_db',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
app.use(bodyParser.json());
// 埋點(diǎn)數(shù)據(jù)接收接口
app.post('/api/track', async (req, res) => {
try {
const { userId, eventType, pageUrl, moduleName } = req.body;
// 1. 記錄原始事件
await pool.query(
'INSERT INTO tracking_events (user_id, event_type, page_url, module_name) VALUES (?, ?, ?, ?)',
[userId, eventType, pageUrl, moduleName]
);
// 2. 如果是頁面訪問,更新每日統(tǒng)計(jì)
if (eventType === 'pageview') {
await updateDailyStats(userId);
}
// 3. 如果是模塊點(diǎn)擊,更新模塊統(tǒng)計(jì)
if (eventType === 'module_click' && moduleName) {
await updateModuleStats(moduleName);
}
res.status(200).send('OK');
} catch (error) {
console.error('Tracking error:', error);
res.status(500).send('Internal Server Error');
}
});
// 更新每日統(tǒng)計(jì)
async function updateDailyStats(userId) {
const today = new Date().toISOString().split('T')[0];
// 檢查今天是否已有記錄
const [rows] = await pool.query('SELECT * FROM daily_stats WHERE date = ?', [today]);
if (rows.length === 0) {
// 新的一天,創(chuàng)建新記錄
await pool.query(
'INSERT INTO daily_stats (date, total_visits, unique_visitors) VALUES (?, 1, 1)',
[today]
);
} else {
// 更新現(xiàn)有記錄
// 檢查用戶今天是否已經(jīng)訪問過
const [visits] = await pool.query(
'SELECT COUNT(DISTINCT user_id) as user_visited FROM tracking_events ' +
'WHERE event_type = "pageview" AND DATE(created_at) = ? AND user_id = ?',
[today, userId]
);
const isNewVisitor = visits[0].user_visited === 0;
await pool.query(
'UPDATE daily_stats SET total_visits = total_visits + 1, ' +
'unique_visitors = unique_visitors + ? WHERE date = ?',
[isNewVisitor ? 1 : 0, today]
);
}
}
// 更新模塊統(tǒng)計(jì)
async function updateModuleStats(moduleName) {
await pool.query(
'INSERT INTO module_stats (module_name, click_count) VALUES (?, 1) ' +
'ON DUPLICATE KEY UPDATE click_count = click_count + 1',
[moduleName]
);
}
// 獲取統(tǒng)計(jì)數(shù)據(jù)的接口
app.get('/api/stats', async (req, res) => {
try {
// 總訪問量
const [totalVisits] = await pool.query('SELECT SUM(total_visits) as total FROM daily_stats');
// 今日訪問量
const today = new Date().toISOString().split('T')[0];
const [todayStats] = await pool.query('SELECT * FROM daily_stats WHERE date = ?', [today]);
// 熱門模塊
const [popularModules] = await pool.query(
'SELECT module_name, click_count FROM module_stats ORDER BY click_count DESC LIMIT 5'
);
res.json({
totalVisits: totalVisits[0].total || 0,
todayVisits: todayStats[0] ? todayStats[0].total_visits : 0,
todayUniqueVisitors: todayStats[0] ? todayStats[0].unique_visitors : 0,
popularModules: popularModules
});
} catch (error) {
console.error('Error fetching stats:', error);
res.status(500).send('Internal Server Error');
}
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
三、數(shù)據(jù)可視化
可以創(chuàng)建一個(gè)管理后臺(tái)頁面來展示這些統(tǒng)計(jì)數(shù)據(jù):
<template>
<div class="stats-dashboard">
<h1>網(wǎng)站訪問統(tǒng)計(jì)</h1>
<div class="stats-grid">
<div class="stat-card">
<h3>總訪問量</h3>
<p class="stat-value">{{ stats.totalVisits }}</p>
</div>
<div class="stat-card">
<h3>今日訪問量</h3>
<p class="stat-value">{{ stats.todayVisits }}</p>
</div>
<div class="stat-card">
<h3>今日獨(dú)立訪客</h3>
<p class="stat-value">{{ stats.todayUniqueVisitors }}</p>
</div>
</div>
<div class="popular-modules">
<h2>熱門模塊</h2>
<ul>
<li v-for="(module, index) in stats.popularModules" :key="index">
{{ module.module_name }}: {{ module.click_count }} 次點(diǎn)擊
</li>
</ul>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
stats: {
totalVisits: 0,
todayVisits: 0,
todayUniqueVisitors: 0,
popularModules: []
}
};
},
mounted() {
this.fetchStats();
},
methods: {
async fetchStats() {
try {
const response = await axios.get('/api/stats');
this.stats = response.data;
} catch (error) {
console.error('Error fetching stats:', error);
}
}
}
};
</script>
<style>
.stats-dashboard {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: #f5f5f5;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.stat-value {
font-size: 24px;
font-weight: bold;
margin: 10px 0 0;
}
.popular-modules ul {
list-style: none;
padding: 0;
}
.popular-modules li {
padding: 10px;
background: #f0f0f0;
margin-bottom: 5px;
border-radius: 4px;
}
</style>
四、優(yōu)化建議
性能優(yōu)化:
- 前端可以使用節(jié)流(throttle)或防抖(debounce)技術(shù)減少高頻點(diǎn)擊的埋點(diǎn)請求
- 后端可以考慮使用批量插入代替單條插入
數(shù)據(jù)準(zhǔn)確性:
- 使用更可靠的用戶識別方式,如結(jié)合IP、設(shè)備指紋等
- 考慮使用Web Beacon API在頁面卸載時(shí)發(fā)送數(shù)據(jù)
擴(kuò)展性:
- 可以添加更多事件類型(如停留時(shí)長、滾動(dòng)深度等)
- 可以按時(shí)間段(小時(shí)/天/周)分析訪問模式
隱私合規(guī):
- 添加用戶同意機(jī)制(如GDPR合規(guī))
- 提供隱私政策說明數(shù)據(jù)收集用途
這個(gè)方案提供了完整的從前端埋點(diǎn)到后端存儲(chǔ)再到數(shù)據(jù)展示的全流程實(shí)現(xiàn),你可以根據(jù)實(shí)際需求進(jìn)行調(diào)整和擴(kuò)展。
以上就是基于Vue+Node.js實(shí)現(xiàn)埋點(diǎn)功能全流程的詳細(xì)內(nèi)容,更多關(guān)于Vue+Node.js埋點(diǎn)功能的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue3+Canvas實(shí)現(xiàn)坦克大戰(zhàn)游戲(二)
本文主要給大家講解一下子彈擊中物體、物體銷毀、敵方坦克構(gòu)建生成、運(yùn)動(dòng)算法、爆炸效果、以及障礙物的生成,感興趣的小伙伴可以了解一下2022-03-03
vue?element-ui的table列表中展示多張圖片(可放大)效果實(shí)例
這篇文章主要給大家介紹了關(guān)于vue?element-ui的table列表中展示多張圖片(可放大)效果的相關(guān)資料,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08
Vue3實(shí)現(xiàn)計(jì)算屬性的代碼詳解
計(jì)算屬性對于前端開發(fā)來說算是經(jīng)常使用的一個(gè)能力了,本文將從代碼層面來給大家介紹下Vue3是如何實(shí)現(xiàn)計(jì)算屬性的,需要的朋友可以參考下2023-07-07
vue項(xiàng)目實(shí)現(xiàn)中英文切換的詳細(xì)步驟
這篇文章主要給大家介紹了關(guān)于vue項(xiàng)目實(shí)現(xiàn)中英文切換的詳細(xì)步驟,項(xiàng)目中經(jīng)常有中英文切換的功能,接下來就簡單實(shí)現(xiàn)以下這個(gè)功能,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考2023-11-11
vue使用rem實(shí)現(xiàn) 移動(dòng)端屏幕適配
這篇文章主要介紹了vue使用rem實(shí)現(xiàn) 移動(dòng)端屏幕適配的相關(guān)知識,通過實(shí)例代碼介紹了vue用rem布局的實(shí)現(xiàn)代碼,需要的朋友可以參考下2018-09-09
webpack 3 + Vue2 使用dotenv配置多環(huán)境的步驟
這篇文章主要介紹了webpack 3 + Vue2 使用dotenv配置多環(huán)境,env文件在配置文件都可以用, vue頁面用的時(shí)候需要在 webpack.base.conf.js 重新配置,需要的朋友可以參考下2023-11-11
vue 解決無法對未定義的值,空值或基元值設(shè)置反應(yīng)屬性報(bào)錯(cuò)問題
這篇文章主要介紹了vue 解決無法對未定義的值,空值或基元值設(shè)置反應(yīng)屬性報(bào)錯(cuò)問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07

