信息发布→ 登录 注册 退出

Vue手动埋点设计的方法实例

发布时间:2026-01-11

点击量:
目录
  • 目标
  • 简述
  • 按页面管理埋点
    • 实现上面的调用
  • 埋点设置支持对象形式
    • 将对象格式的埋点配置转成方法形式的
  • 提供页面级别的参数设置
    • 总结

      目标

      • 使用简单;
      • 减少代码侵入性,不影响业务代码阅读

      简述

      • 埋点一般是按页面来管理的;
      • 埋点的事件类型一般分为:点击、曝光和页面的进入和离开;
      • 埋点的实质就是在恰当的时机去发送请求,上送业务参数

      按页面管理埋点

      在每个页面目录下创建events.js,管理当前页面的所有埋点事件。

      为了减少埋点对业务代码的影响,events.js中每个埋点的设置都是一个方法,可以在这个方法中处理数据得到埋点需要上送的数据。

      该埋点设置的方法,返回一个埋点需要上送的参数的对象。

      // src/views/PageA/events.js
      
      export default {
        // 按事件类型管理
        CLICK: {
          back(ctx, data) {
            let { param1, param2 } = data;
            // ...处理传入的数据
            return {
              eventValue: '返回',
              elementId: 'pageA-返回',
              // 需要上送的处理后的业务参数
              customData: {
                param1,
                param2
              }
            };
          }
        }
      }

      遵循使用简单的原则,调用埋点

      // src/views/PageA/index.vue
      
      this.$track('CLICK.back', data);

      实现上面的调用

      • 使用require.context()聚合各个页面目录下的埋点设置(events.js)。
      • 聚合后的埋点设置按页面作为模块管理,使用页面文件夹名称作为模块名。
      • 结合路由管理,可以获得当前页面的埋点配置模块名。
      • 在Vue.prototype下新增一个$track()方法。
      // src/events/index.js
      import router from '@/router';
      
      const ctx = require.context('@/views', true, /events\.js/);
      const configs = {};
      ctx.keys().reduce((configs, path) => {
        if (/\.\/(\w+?)\/events\.js/.test(path)) {
          const moduleName = RegExp.$1; // 第一个子项
          configs[moduleName] = resolveModule(moduleName, ctx(path));
        }
        return configs;
      }, configs);
      
      function resolveModule(moduleName, module) {
        return Object.entries(module.default).reduce((all, [EVENT_TYPE, events]) => {
          all[EVENT_TYPE] = Object.keys(events).reduce((typeAll, key) => {
            typeAll[key] = buildTrackRequest(
              EVENT_TYPE.toLowerCase(),
              key,
              events[key]
            );
            return typeAll;
          }, {});
        });
      }
      
      function buildTrackRequest(eventType, trackName, trackSetting) {
        return function trackRequest(...args) {
          // 看完后面再回过来看
          if (typeof trackSetting !== 'function') {
            trackSetting = obj2fn(trackSetting);
          }
          // 执行用户定义的方法,返回埋点上送参数
          const [success, result] = invokeUserFn(trackSetting.bind(this, {}));
          if (!success) return result;
          // 传入参数,发送埋点请求
          return tracker(result);
        }
      }
      
      export function track(eventPath, ...args) {
        let event = configs;
        let seg;
        const segs = eventPath.split('.');
        // 2段式 没有提供模块名,则需要去路由配置上取
        if (segs.length === 2) {
          const moduleName = router.currentRoute.meta?.eventModule;
          if (!moduleName) {
            throwError(`${eventPath} 没有在路由配置中设置"meta.eventModule" 或者配置成3段式`);
          }
          event = event[moduleName];
        }
        while ((seg = segs.shift())) event = event[seg];
        if (!event) throwError(`${eventPath} 不存在`);
        // 给event绑定当前调用环境
        return event.call(this, ...args);
      }
      
      function throwError(err) {
        throw Error(`[Track Error] ${err}`);
      }
      
      export default function install(Vue) {
        Vue.prototype.$track = track;
      }

      埋点设置支持对象形式

      很多时候,可能不需要上送业务参数,写成一个对象更加简单。

      {
        CLICK: {
          back: {
            eventValue: '返回',
            elementId: 'pageA-返回',
          }
        }
      }

      有时候只需要上送简单的业务字段,无需额外处理,也想使用对象的形式。

      支持{{param1}}模板语法,同vue-template用法。(param1是埋点调用组件的属性)

      {
        CLICK: {
          back: {
            eventValue: '返回',
            elementId: 'pageA-返回',
            customData: {
              param1: '{{param1}}'
            }
          }
        }
      }

      将对象格式的埋点配置转成方法形式的

      const templateRE = /\{\{(.+?)\}\}/g;
      // 处理对象形式的埋点设置
      function obj2fn(obj) {
        return function() {
          const that = this;
          // 处理模板字符串
          (function resolveObj(obj) {
            Object.keys(obj).forEach(key => {
              const val = obj[key];
              // 解析模板字符串
              if (typeof val === 'string' && templateRE.test(val)) {
                obj[key] = val.replace(templateRE, (...match) => {
                  // js严格模式下无法执行with语法,以下是一种变通
                  return new Function(`with(this){return ${match[1]}}`).call(that);
                });
              }
              // 递归处理
              else if (isPlainObject(val)) resolve(val);
            });
          })(obj);
          return obj;
        };
      }

      提供页面级别的参数设置

      很多时候一个页面下的埋点都需要上送相同的参数,如页面名称等。

      提供beforeModuleEach和afterModuleEach两个钩子。

      一般使用beforeModuleEach设置模块(页面)通用的埋点参数,再合并单独的埋点设置参数,得到所有需要上送的参数。

      function resolveModule(moduleName, module) {
        // 获取`events.js`文件中设置的钩子
        const { beforeModuleEach, afterModuleEach } = module;
        // 获取动态设置钩子
        const { beforeHooks, afterHooks } = getHooksByModule(moduleName);
        beforeModuleEach && beforeHooks.unshift(beforeModuleEach);
        afterModuleEach && afterHooks.unshift(afterModuleEach);
        
        return Object.entries(module.default).reduce((all, [EVENT_TYPE, events]) => {
          all[EVENT_TYPE] = Object.keys(events).reduce((typeAll, key) => {
            typeAll[key] = buildTrackRequest(
              EVENT_TYPE.toLowerCase(),
              key,
              events[key],
              beforeHooks,
              afterHooks
            );
            return typeAll;
          }, {});
        });
      }

      总结

      在线客服
      服务热线

      服务热线

      4008888355

      微信咨询
      二维码
      返回顶部
      ×二维码

      截屏,微信识别二维码

      打开微信

      微信号已复制,请打开微信添加咨询详情!