auto-tracking自动埋点插件
自动埋点插件使用说明
概述
auto-tracking.js 是一个 Vue 插件,用于自动为页面上的所有按钮添加埋点标识,无需手动在每个按钮上添加 v-tracking 指令或 data-warden-title 属性。
功能特性
- ✅ 完全自动化:无需手动配置,自动为所有按钮添加埋点
 - ✅ 智能识别:根据按钮文本自动推断操作类型
 - ✅ 路由集成:自动获取路由 
meta.title作为模块名 - ✅ 优先级保护:如果按钮已手动设置埋点,则跳过自动处理
 - ✅ 动态支持:支持动态添加的按钮(通过 MutationObserver)
 - ✅ 路由响应:路由切换时自动更新埋点标识
 
安装使用
1. 插件已在 main.js 中注册
import autoTracking from '@/plugins/auto-tracking';
Vue.use(autoTracking);
2. 无需任何额外配置
插件会自动工作,为所有 el-button 按钮自动添加埋点。
使用示例
基础用法
之前需要手动添加埋点:
<!-- 之前:需要手动添加 -->
<el-button v-tracking="'query'" @click="handleQuery">查询</el-button>
<el-button:data-warden-title="'用户管理-导出'"@click="handleExport"
>导出</el-button>
现在完全不需要:
<!-- 现在:自动添加埋点 -->
<el-button @click="handleQuery">查询</el-button>
<el-button @click="handleExport">导出</el-button>
插件会自动生成埋点标识:
查询按钮 →当前页面title-查询导出按钮 →当前页面title-导出
保留手动配置
如果某个按钮需要手动指定埋点标识,可以继续使用手动方式,插件会跳过自动处理:
<!-- 手动设置,插件不会覆盖 -->
<el-button:data-warden-title="'用户管理-自定义查询'"@click="handleQuery"
>查询</el-button>
<el-buttonv-tracking="{ action: 'export', module: '自定义模块' }"@click="handleExport"
>导出</el-button>
排除某些按钮
如果某个按钮不需要埋点,可以添加 data-no-tracking 属性:
<!-- 排除埋点 -->
<el-button data-no-tracking @click="handleCancel">取消</el-button>
自动识别的操作类型
插件内置了常见按钮文本到操作类型的映射:
| 按钮文本 | 操作类型 | 
|---|---|
| 查询、搜索 | 查询 | 
| 导出 | 导出 | 
| 下载 | 下载 | 
| 删除、移除 | 删除 | 
| 新增、添加、创建 | 新增 | 
| 编辑、修改、更新 | 修改 | 
| 同步 | 同步 | 
| 刷新 | 刷新 | 
| 重置、清空 | 重置 | 
| 导入、上传 | 导入 | 
| 启动、开始 | 启动 | 
| 暂停、停止 | 暂停 | 
| 保存、确认、提交 | 保存 | 
| 取消、关闭 | 取消 | 
| 查看、详情 | 查看 | 
| 复制 | 复制 | 
| 打印 | 打印 | 
| … | … | 
埋点标识格式
自动生成的埋点标识格式为:
{路由meta.title}-{操作类型}
例如:
- 路由 title 为 
用户管理,按钮文本为查询→用户管理-查询 - 路由 title 为 
系统配置,按钮文本为新增→系统配置-新增 
注意事项
- 路由配置:确保路由配置中包含 
meta.title,否则会使用未知页面作为模块名 - 国际化支持:如果按钮文本使用了国际化(
$t()),插件会自动识别 - 动态按钮:插件支持动态添加的按钮,会自动为其添加埋点
 - 性能:使用防抖机制,避免频繁触发,不会影响页面性能
 
工作原理
- 插件在 Vue 实例挂载时自动运行
 - 使用 
MutationObserver监听 DOM 变化 - 为所有 
.el-button元素自动添加data-warden-title属性 - 如果元素已有 
data-warden-title或使用了v-tracking,则跳过 - 路由切换时,自动更新所有按钮的埋点标识
 
与现有方案兼容
- ✅ 与 
v-tracking指令完全兼容(手动设置的优先级更高) - ✅ 与 
TrackButton组件兼容(组件内部已设置埋点的不会被覆盖) - ✅ 与手动设置的 
data-warden-title兼容 
排除规则
以下按钮会被自动排除:
- 已有 
data-warden-title属性的按钮 - 使用了 
v-tracking指令的按钮 - 有 
data-no-tracking属性的按钮 - 分页组件中的按钮
 - 对话框关闭按钮
 - 消息框按钮
 
/*** 自动埋点插件* 无需手动在按钮上添加埋点信息,自动根据按钮文本和路由信息生成埋点标识** 功能:* 1. 自动为所有 el-button 按钮添加埋点* 2. 根据按钮文本自动推断操作类型* 3. 自动获取路由 title 作为模块名* 4. 如果按钮已手动设置了 data-warden-title 或使用了 v-tracking,则跳过自动处理** 使用方式:* 在 main.js 中引入并注册:Vue.use(autoTracking)*/// 按钮文本到操作类型的映射
const TEXT_TO_ACTION = {// 基础操作查询: '查询',搜索: '查询',导出: '导出',下载: '下载',删除: '删除',移除: '删除',新增: '新增',添加: '新增',创建: '新增',编辑: '修改',修改: '修改',更新: '修改',同步: '同步',刷新: '刷新',重置: '重置',清空: '重置',导入: '导入',上传: '导入',启动: '启动',开始: '启动',暂停: '暂停',停止: '暂停',保存: '保存',确认: '保存',提交: '保存',取消: '取消',关闭: '取消',查看: '查看',详情: '查看',复制: '复制',打印: '打印',// 扩展操作批量删除: '批量删除',批量导出: '批量导出',批量导入: '批量导入',批量操作: '批量操作',一键采集: '一键采集',采集: '采集',获取: '获取',审批: '审批',拒绝: '拒绝',订阅: '订阅',取消订阅: '取消订阅',
};// 需要排除的选择器(避免对不需要埋点的按钮添加)
const EXCLUDE_SELECTORS = ['el-pagination .el-button', // 分页按钮'el-dialog__headerbtn', // 对话框关闭按钮'.el-message-box__btns .el-button', // 消息框按钮'[data-no-tracking]', // 手动标记不需要埋点的元素
];// 判断元素是否应该被排除
function shouldExclude(element) {if (!element) return true;// 检查是否已有手动设置的埋点if (element.hasAttribute('data-warden-title')) {return true;}// 检查是否使用了 v-tracking 指令(Vue会添加这个属性)if (element.__v_tracking || element.getAttribute('v-tracking')) {return true;}// 检查排除选择器for (const selector of EXCLUDE_SELECTORS) {if (element.closest(selector)) {return true;}}// 检查是否有 data-no-tracking 属性if (element.hasAttribute('data-no-tracking')) {return true;}return false;
}// 获取按钮文本
function getButtonText(element, vueInstance) {// 获取按钮的所有文本内容let text = element.textContent?.trim() || element.innerText?.trim() || '';// 清理文本:移除多余的空白字符text = text.replace(/\s+/g, ' ').trim();// 如果有 Vue 实例,尝试使用国际化if (vueInstance && vueInstance.$t && text) {try {// 尝试将文本作为国际化键const i18nText = vueInstance.$t(text);if (i18nText && i18nText !== text) {text = i18nText;}} catch (e) {// 忽略错误,继续使用原始文本}}return text;
}// 生成埋点标识
function generateTrackingTitle(buttonText, routeTitle) {if (!buttonText) return '';// 从按钮文本推断操作类型const action = TEXT_TO_ACTION[buttonText] || buttonText;// 生成埋点标识:路由title-操作类型return `${routeTitle}-${action}`;
}// 自动为按钮添加埋点
function addAutoTracking(element, vueInstance) {if (shouldExclude(element)) {return;}const buttonText = getButtonText(element, vueInstance);if (!buttonText) {return;}// 获取路由 titleconst routeTitle =vueInstance?.$router?.currentRoute?.meta?.title || '未知页面';// 生成埋点标识const trackingTitle = generateTrackingTitle(buttonText, routeTitle);if (trackingTitle) {element.setAttribute('data-warden-title', trackingTitle);}
}// 使用 MutationObserver 监听 DOM 变化,自动为新添加的按钮添加埋点
function setupMutationObserver(vueInstance) {// 使用防抖,避免频繁触发let timeout = null;const processNodes = (nodes) => {clearTimeout(timeout);timeout = setTimeout(() => {nodes.forEach((node) => {if (node.nodeType === 1) {// Element node// 检查是否是按钮if (node.classList && node.classList.contains('el-button')) {vueInstance.$nextTick(() => {addAutoTracking(node, vueInstance);});}// 检查子元素中的按钮if (node.querySelectorAll) {const buttons = node.querySelectorAll('.el-button');buttons.forEach((button) => {vueInstance.$nextTick(() => {addAutoTracking(button, vueInstance);});});}}});}, 100); // 延迟100ms执行,等待Vue渲染完成};const observer = new MutationObserver((mutations) => {const addedNodes = [];mutations.forEach((mutation) => {mutation.addedNodes.forEach((node) => {addedNodes.push(node);});});if (addedNodes.length > 0) {processNodes(addedNodes);}});observer.observe(document.body, {childList: true,subtree: true,});return observer;
}// 为现有按钮添加埋点
function trackExistingButtons(vueInstance) {// 延迟执行,确保 Vue 实例已完全初始化vueInstance.$nextTick(() => {const buttons = document.querySelectorAll('.el-button');buttons.forEach((button) => {addAutoTracking(button, vueInstance);});});
}export default {install(Vue) {// 在 Vue 实例创建后自动添加埋点Vue.mixin({mounted() {// 只在根组件执行一次if (this.$root === this) {// 为现有按钮添加埋点trackExistingButtons(this);// 设置 MutationObserver 监听新添加的按钮this._autoTrackingObserver = setupMutationObserver(this);// 监听路由变化,更新埋点(因为模块名可能改变)if (this.$router) {this.$router.afterEach(() => {this.$nextTick(() => {const buttons = document.querySelectorAll('.el-button');buttons.forEach((button) => {// 只更新没有手动设置的按钮if (!button.hasAttribute('data-warden-title') &&!button.hasAttribute('data-no-tracking')) {addAutoTracking(button, this);}});});});}}},beforeDestroy() {// 清理 MutationObserverif (this.$root === this && this._autoTrackingObserver) {this._autoTrackingObserver.disconnect();}},});},
};