基于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)建一個管理后臺頁面來展示這些統(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í)長、滾動深度等)
- 可以按時(shí)間段(小時(shí)/天/周)分析訪問模式
隱私合規(guī):
- 添加用戶同意機(jī)制(如GDPR合規(guī))
- 提供隱私政策說明數(shù)據(jù)收集用途
這個方案提供了完整的從前端埋點(diǎn)到后端存儲再到數(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)文章
淺談vue自定義全局組件并通過全局方法 Vue.use() 使用該組件
本篇文章主要介紹了vue自定義全局組件并通過全局方法 Vue.use() 使用該組件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Vue2.5學(xué)習(xí)筆記之如何在項(xiàng)目中使用和配置Vue
這篇文章主要介紹了Vue2.5學(xué)習(xí)筆記之如何在項(xiàng)目中使用和配置Vue的相關(guān)知識,非常不錯,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09vue實(shí)現(xiàn)的上拉加載更多數(shù)據(jù)/分頁功能示例
這篇文章主要介紹了vue實(shí)現(xiàn)的上拉加載更多數(shù)據(jù)/分頁功能,涉及基于vue的事件響應(yīng)、數(shù)據(jù)交互等相關(guān)操作技巧,需要的朋友可以參考下2019-05-05vue 設(shè)置 input 為不可以編輯的實(shí)現(xiàn)方法
今天小編就為大家分享一篇vue 設(shè)置 input 為不可以編輯的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09