vue项目前端埋点的实现

埋点方案的确定。业界的埋点方案主要分为以下三类:

  • 代码埋点:在需要埋点的节点调用接口,携带数据上传。如百度统计等;
  • 可视化埋点:使用可视化工具进行配置化的埋点,即所谓的「无痕埋点」,前端在页面加载时,可以读取配置数据,自动调用接口进行埋点。如开源的Mixpanel;
  • 无埋点:前端自动采集全部事件并上报埋点数据。如国内的神策数据等;

在当时排期紧凑,人力紧缺的情况下,显然不允许我们去开发可视化埋点方案和无埋点方案,所以只能采取代码埋点方案。

命令式埋点

命令式埋点,顾名思义,开发者需要手动在需要埋点的节点处进行埋点。如点击按钮或链接后的回调函数、页面ready时进行请求的发送。大家肯定都很熟悉这样的代码:

// 页面加载时发送埋点请求
$(document).ready(function(){
  // ... 这里存在一些业务逻辑
  sendRequest(params);
});
// 按钮点击时发送埋点请求
$('button').click(function(){
  // ... 这里存在一些业务逻辑
  sendRequest(params);
});

可以很容易发现,这样的做法很有可能会将埋点代码侵入业务代码,这使整体业务代码变得繁琐,容易出错,且后续代码会愈加膨胀,难以维护。所以,我们需要让埋点的代码与具体的业务逻辑解耦,即 声明式埋点 ,从而提高埋点的效率和代码的可维护性。

声明式埋点

理论上,声明式埋点只需要关注两个问题:

  • 需要埋点的DOM节点;
  • 所需携带的数据

因此,可以很快想出一个声明式埋点的方法:

// key表示埋点的唯一标识;act表示埋点方式
<button data-stat="{key:'111', act: 'click'}">埋点</button>

那么可以去遍历DOM树,找到 [data-stat] 的节点,给这个button绑上click事件,把这些参数在回调函数中通过请求发出去。

在DOM节点(html)上声明埋点,与业务逻辑(通常在Javascript文件中)就解耦了。调用也很方便。

看起来很美,但这样就能解决问题了吗?显然是不够的。还需要解决以下问题:

  • 遍历DOM树的时机问题,一个简单的例子,一个表格的行数据是通过异步加载,而表格行中的操作按钮需要埋点,那么在DOM ready的时候去遍历,显然是无法找到的
  • 绑定埋点事件次数的问题,怎样保证埋点事件不会被重复绑定到元素上,一次操作发了N个埋点请求?
  • 如何处理特有的埋点行为,如页面展现埋点,区域展现埋点?
  • 如何在解绑时,销毁已绑定的事件?

1.自定义指令实现埋点数据统计

在项目中通常需要做数据埋点,这个时候,使用自定义指令将会变非常简单

在项目入口文件 main.js 中配置我们的自定义指令

// 坑位埋点指令
Vue.directive('stat', {
 bind(el, binding) {
  el.addEventListener('click', () => {
   const data = binding.value;
   let prefix = 'store';
   if (OS.isAndroid || OS.isPhone) {
    prefix = 'mall';
   }
   analytics.request({
    ty: `${prefix}_${data.type}`,
    dc: data.desc || ''
   }, 'n');
  }, false);
 }
});

2.使用路由拦截统计页面级别的 PV

由于第一次在单页应用中尝试数据埋点,在项目上线一个星期之后,数据统计后台发现,首页的 PV 远远高于其它页面,数据很不正常。后来跟数据后台的人沟通询问他们的埋点统计原理之后,才发现其中的问题所在。

传统应用,一般都在页面加载的时候,会有一个异步的 js 加载,就像百度的统计代码类似,所以我们每个页面的加载的时候,都会统计到数据;然而在单页应用,页面加载初始化只有一次,所以其它页面的统计数据需要我们自己手动上报

解决方案

使用 vue-router 的 beforeEach 或者 afterEach 钩子上报数据,具体使用哪个最好是根据业务逻辑来选择。

const analyticsRequest = (to, from) => {
 // 只统计页面跳转数据,不统计当前页 query 不同的数据
 // 所以这里只使用了 path, 如果需要统计 query 的,可以使用 to.fullPath
 if (to.path !== from.path) {
  analytics.request({
   url: `${location.protocol}//${location.host}${to.path}`
  });
 }
};

router.beforeEach((to, from, next) => {
 if (to.matched.some(record => record.meta.requiresAuth)) {
  // 这里做登录等前置逻辑判断
  // 判断通过之后,再上报数据
  ...
  analyticsRequest(to, from);
 } else {
  // 不需要判断的,直接上报数据
  analyticsRequest(to, from);
  next();
 }
});

在组件中使用我们的自定义指令

基于 jquery + widget 的老项目,

那么在这些项目中的DOM操作是jquery甚至原生DOM API来实现,Vue的自定义指令就无法工作

基于MutationObserver API的Mixin

MutationObserver是在DOM3标准中提出的标准API,提供让开发者感知到在某一个DOM节点变更的能力。可以监听以下场景:

  • childList: 目标节点的子节点插入删除引起的变更
  • attributes: 目标节点属性改变引起的变更
  • characterData: 目标节点的文本节点改变引起的变更,如通过appendData()等
  • subtree: 目标节点的子孙节点改变引起的变更
  • attributeOldValue:当attribute监听被设定为true时,可以记录改变前的属性值
  • characterDataOldValue:当characterData监听被设定为true时,可以记录改变前的属性值
  • attributeFilter:可以设定需要监听的属性列表

但为了保证MutationObserver可以在所有浏览器上正常工作,我们仍然引入了这个API的polyfill,详情可见这里

在此能力的前提下,我们就可以在任意的DOM操作下触发Vue进行重新解析指令。

我们将 MutationObserver 封装进一个 Vue mixin , 非Vue应用的业务代码只需要引入这个mixin,这样也可以很好地解耦。
详细的实现原理可以见以下伪代码:

let observer;
export default {
 ready() {
  // 开启监听
  observer = new MutationObserver(mutations => {
   this.$compile(this.$el);
  });
  observer.observe(this.$el, config);
 },
 destroyed() {
  // 清理工作
  observer.disconnect();
  observer.takeRecords();
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • vue项目前端埋点的实现

    埋点方案的确定.业界的埋点方案主要分为以下三类: 代码埋点:在需要埋点的节点调用接口,携带数据上传.如百度统计等: 可视化埋点:使用可视化工具进行配置化的埋点,即所谓的「无痕埋点」,前端在页面加载时,可以读取配置数据,自动调用接口进行埋点.如开源的Mixpanel; 无埋点:前端自动采集全部事件并上报埋点数据.如国内的神策数据等: 在当时排期紧凑,人力紧缺的情况下,显然不允许我们去开发可视化埋点方案和无埋点方案,所以只能采取代码埋点方案. 命令式埋点 命令式埋点,顾名思义,开发者需要手动在需要埋

  • vue项目前端知识点整理【收藏】

    微信授权后还能通过浏览器返回键回到授权页 在导航守卫中可以在 next({}) 中设置 replace: true 来重定向到改路由,跟 router.replace() 相同 router.beforeEach((to, from, next) => { if (getToken()) { ... } else { // 储存进来的地址,供授权后跳回 setUrl(to.fullPath) next({ path: '/author', replace: true }) } }) 路由切换时页

  • vue项目前端错误收集之sentry教程详解

    sentry简介 Sentry 是一个开源的错误追踪工具,可以帮助开发人员实时监控和修复系统中的错误.其专注于错误监控以及提取一切事后处理所需的信息;支持几乎所有主流开发语言( JS/Java/Python/php )和平台, 并提供了web来展示输出错误. sentry官网: https://sentry.io/ sentry安装 sentry 是一个开源的工具,可以自行搭建. 官方支持两种安装和运行 Sentry 服务器的方法, Docker 和 Python .推荐使用 Docker .

  • vue项目前端微信JSAPI与外部H5支付相关实现过程及常见问题

    web端的微信支付可分为JSAPI与H5 JSAPI使用场景为微信内部,而H5支付则是使用在非微信下的外部浏览器. 1.JSAPI 在微信内部可以直接唤起微信浏览器的内置对象 WeixinJSBridge 废话不多说直接上代码: // 调用后端接口拿到JSAPI支付所需参数,我在beforeRouteEnter的时候调用. api.apply.jsApiPay(obj).then(res => { if (res.data.code === '0000') { vm.params = res.d

  • 如何通过Vue自定义指令实现前端埋点详析

    目录 前言 埋点上报方式都有哪些? 一般对哪些数据做埋点? 需求分析 代码实现 Click 类封装 Exposure 类封装 指令封装 使用 不足 总结 前言 在营销活动中,通过埋点可以获取用户的喜好及交互习惯,从而优化流程,进一步提升用户体验,提高转化率. 在之前的埋点方案实现中,都是在具体的按钮或者图片被点击或者被曝光时主动通过事件去上报埋点.这种方法在项目中埋点比较少时还行,一旦项目中需要大量埋点时,不可避免的要添加很多业务代码.也很大程度上造成了埋点逻辑与业务逻辑的高耦合. 为了改造这种

  • vue项目打包发布后接口报405错误的解决

    目录 vue项目打包发布后接口报405 vue项目打包之后接口出现错误问题 错误信息 关键代码 解决方式 vue项目打包发布后接口报405 vue项目前端做了代理打包后后台不识别报405 not allowed vue.config.js文件配置   devServer: {     // host: "0.0.0.0", //项目运行时的本地地址     // port: 8880, // 端口号     //proxy:{'/api':{}},代理器中设置/api,项目中请求路径为

  • Vue.js项目前端多语言方案的思路与实践

    目录 一.通常有哪些内容需要处理 二.基本思路 三.具体实践中的一些细节 1.获取当前应该采用何种语言的getLang模块的实现 2.Vux组件的多语言包的配置 3.vux-loader的配置 4.自定义组件内外文案的多语言化 5.vuex-i18n的实现 6.图片的多语言化 7.在当前页面通过按钮切换当前语言后,如何更新当前页面的内容? 8.Yaml中特殊字符的转义 总结 前端的国际化是一个比较常见的需求.但网上关于这一方面的直接可用的方案却不多.最近刚做了一版基于Vue.js的多语言实现,在

  • Vue项目中token验证登录(前端部分)

    本文实例为大家分享了Vue项目中token验证登录的具体代码,供大家参考,具体内容如下 1.前言 最近在做毕业设计,我是做后端的,前端并不是很懂,看vue这个框架看了近两个礼拜才有点入门的感觉,所以这篇文章写的可能不怎么好,仅作记录,有什么不对或不足的地方欢迎大神指出. 2.问题 做一个登录界面,我选择的是用token进行验证登录,我用的前端框架是Vue.js 和 element-ui,如何在vue 中使用token进行验证登录 3.思考 1.利用token进行验证登录,用户进行登录操作时,后台

  • Vue项目优化打包之前端必备加分项

    目录 前言 一.路由懒加载 1. 为什么需要路由懒加载 2. 如何实现路由懒加载 3. 路由懒加载中的魔法注释 二.分析包大小 1. 需求 2. 如何生成打包分析文件 三.webpack配置排除打包 1. 需求 2. 排除打包 四. 引用网络资源 1. 需求 2. CDN 3. 实现步骤 五. 打包去除console.log 1. 需求 2. 代码演示 总结 前言 Vue项目开发完毕后,对项目进行打包发布之前,必不可少的操作就是项目优化,这也是程序猿的加分项.跟随本文的脚步来看看如何对项目进行优

  • 如何监听Vue项目报错的4种方式 

    目录 背景 onerror element.onerror errorHandler errorCaptured error传播规则(划重点) 如何监听异步错误 总结 背景 在开发Vue项目时,使用浏览器调试可以比较清晰的看到报的什么错.在哪报错,或者使用console.log()打印出报错信息,以便快速定位到报错源头并解决,但是如果项目上线了又怎么查看呢.对于上线项目来说,一般都是会有代码混淆以及禁用console.log(),这个时候再使用浏览器调试就有点不太方便了.另一种场景,如果要做一个

随机推荐