通过Vue自定义指令实现前端埋点

在营销活动中,通过埋点可以获取用户的喜好及交互习惯,从而优化流程,进一步提升用户体验,提高转化率。

在之前的埋点方案实现中,都是在具体的按钮或者图片被点击或者被曝光时主动通过事件去上报埋点。这种方法在项目中埋点比较少时还行,一旦项目中需要大量埋点时,不可避免的要添加很多业务代码。也很大程度上造成了埋点逻辑与业务逻辑的高耦合。

为了改造这种情况,我们对于原有的埋点方式做了一些小改进,使得埋点效率得到了极大提升。

在阐述我们的埋点改造之前,有必要对埋点的一些常识做下简单的了解。

埋点上报方式都有哪些?

要知道埋点的类型有很多,上报的方式也是五花八门。前端常见的埋点方法有三种:

  • 手动埋点
  • 可视化埋点
  • 无痕埋点

手动埋点,顾名思义就是纯手动写代码,调用埋点 SDK 提供的函数,在需要埋点的业务逻辑中添加对应方法,上报埋点数据。这种也是之前一直在使用的方法。

可视化埋点是指通过可视化系统配置埋点,这种方式接触的不是很多,就不展开说了。

无痕埋点,也叫自动埋点、全埋点。即对全局所有事件和页面加载周期进行拦截埋点。

一般对哪些数据做埋点?

为了达到数据分析,便于后续的运营及产品策略调整的目的,一般需要对以下几点做埋点统计:

  • 页面埋点:统计用户进入或者离开页面的信息,如页面浏览次数(pv)、浏览页面人数(uv)、页面停留时长、设备信息等
  • 点击埋点:统计用户在页面浏览过程中触发的点击事件,如按钮、导航或者图片的点击次数
  • 曝光埋点:统计具体元素是否得到有效曝光

需求分析

本文是基于最近项目中添加埋点的需求,我们需要的一种理想化方案是:

  • 埋点与业务尽量分离,埋点逻辑更应该是独立于业务的
  • 尽量不对业务代码有侵入
  • 约定规范,通过统一收口来处理埋点逻辑

由于项目是Vue开发的,所以考虑使用自定义指令的方式来完成埋点上报。选择自定义指令的原因也是因为他能一定程度上能让业务和埋点解耦。

页面埋点在框架层面已经帮我们做掉了,这里主要关心的是点击埋点和曝光埋点。

实现思路其实也很清晰:在需要埋点的DOM节点挂载特殊属性,通过埋点SDK监听挂载了相应属性对应的事件,在事件触发时进行埋点数据上报。

那么问题来了,怎么监听呢?

对于点击事件,我们可以采用addEventListener来监听click事件。这很简单。

对于元素的曝光就稍微有点麻烦了。

首先我们来看一下为什么需要监测曝光:

为了衡量用户对产品的兴趣程度,需要计算区域的点击率(点击次数/曝光次数)。为了保证点击率的准确性,我们必须保证用户真正的浏览到了这些产品(就比如上图中最下方的机酒产品区域,由于需要滚动页面,用户才有可能看到这一区域)。

那么怎么判断元素出现在页面的可视区域呢?

按照以往的做法:监听滚动事件,通过getBoundingClientRect()方法计算监测区域与视窗的位置,然后判断元素是否出现在页面的可视区域内。但是由于scroll事件的频繁触发,性能问题很大。

基于此,浏览器特意为我们打造了一个Intersection ObserverAPI,把性能相关的细节都处理掉,让开发者只关心业务逻辑即可:

由于用户浏览页面的不确定性,还必须要避免重复的曝光行为。这个在曝光之后,移除观察即可。

代码实现

上面的需求分析还是比较抽象,下面让我们结合代码来看一下最终的实现。

Click 类封装

点击事件的处理相对比较简单,每次点击触发数据上报即可:

// src/directives/track/click.js
import { sendUBT } from "../../utils/ctrip"export default class Click {add(entry) {// console.log("entry", entry);const traceVal = entry.el.attributes["track-params"].valueconst traceKey = entry.el.attributes["trace-key"].valueconst { clickAction, detail } = JSON.parse(traceVal)const data = {action: clickAction,detail,}entry.el.addEventListener("click", function() {console.log("上报点击埋点", JSON.parse(traceVal))console.log("埋点key", traceKey)sendUBT(traceKey, data)})}
}

Exposure 类封装

曝光的相对复杂一些。

首先通过new IntersectionObserver() 实例化一个全局_observer,如果得到有效曝光的(这里当元素出现一半以上则进行曝光),就去获取 DOM 节点上的trace-key(埋点 key)和track-params(埋点 value)。

// src/directives/track/exposure.js
import "intersection-observer"
import { sendUBT } from "../../utils/ctrip"// 节流时间调整,默认100ms
IntersectionObserver.prototype["THROTTLE_TIMEOUT"] = 300export default class Exposure {constructor() {this._observer = nullthis.init()}init() {const self = this// 实例化监听this._observer = new IntersectionObserver(function(entries, observer) {entries.forEach((entry) => {// 出现在视窗内if (entry.isIntersecting) {// 获取参数// console.log("埋点节点", entry.target.attributes);const traceKey = entry.target.attributes["trace-key"].valueconst traceVal = entry.target.attributes["track-params"].valueconsole.log("traceKey", traceKey)console.log("traceVal", traceVal)const { exposureAction, detail } = JSON.parse(traceVal)const data = {action: exposureAction,detail,}// 曝光之后取消观察self._observer.unobserve(entry.target)self.track(traceKey, data)}})},{root: null,rootMargin: "0px",threshold: 0.5, // 元素出现面积,0 - 1,这里当元素出现一半以上则进行曝光})}/*** 元素添加监听** @param {*} entry* @memberof Exposure*/add(entry) {this._observer && this._observer.observe(entry.el)}/*** 埋点上报** @memberof Exposure*/track(traceKey, traceVal) {// console.log("曝光埋点", traceKey, JSON.parse(traceVal));sendUBT(traceKey, traceVal)}}

指令封装

有了点击和曝光类,下一步就是 Vue 指令的封装了,也是之所以能实现半自动埋点的核心。

这里存在一个场景就是对于同一个按钮或者图片,同时存在既需要点击埋点又需要曝光埋点的场景。所以在指令的设计时支持了单独传入和同时传入的场景:

  • v-track:click|exposure
  • v-track:exposure
// src/directives/track/index.js
import Vue from "vue"
import Click from "./click"
import Exposure from "./exposure"// 实例化曝光和点击
const exp = new Exposure()
const cli = new Click()Vue.directive("track", {bind(el, binding) {// 获取指令参数const { arg } = bindingarg.split("|").forEach((item) => {// 点击if (item === "click") {cli.add({ el })} else if (item === "exposure") {exp.add({ el })}})},
})

同时需要在src/index.js引入即可:

import "./directives/track"

使用

在需要埋点的地方使用也是很简单的:

<imgref="imageDom"trace-key="o_img"v-track:click|exposure:track-params="JSON.stringify({exposureAction: 's_pictures',clickAction: 'c_pictures',detail: {value: '测试',},})"
/>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/595197.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

K8Spod组件

一个pod能包含几个容器 一个pause容器(基础容器/父容器/根容器&#xff09; 一个或者多个应用容器(业务容器) 通常一个Pod最好只包含一个应用容器&#xff0c;一个应用容器最好也只运行一个业务进程。 同一个Pod里的容器都是运行在同一个node节点上的&#xff0c;并且共享 net、…

javaweb学习笔记

JSP 动态网页&#xff0c;指的是随时间、地点、用户操作改变的网页 架构 CS架构 client-server 缺点&#xff1a;每一台客户端都需要安装客户端软件&#xff0c;如果升级全要升级&#xff0c;如果坏了就得维护 优点&#xff1a;响应快&#xff0c;界面美观 BS架构 browser-…

Zookeeper集群 + Kafka集群的详解与部署(以及Filebeat+Kafka+ELK )

zookeeperkafka.txt Zookeeper概述 Zookeeper是一个分布式的开源协调服务&#xff0c;用于管理和维护大型分布式系统中的配置信息、命名服务、状态同步等。它提供了一个可靠的分布式环境&#xff0c;用于协调多个节点之间的通信和管理。 数据结构 ZooKeeper数据模型的结构与…

以STM32为例,实现按键的短按和长按

以STM32为例&#xff0c;实现按键的短按和长按 目录 以STM32为例&#xff0c;实现按键的短按和长按1 实现原理2 实现代码3 测试结束语 1 实现原理 简单来说就是通过设置一个定时器来定时扫描几个按键的状态&#xff0c;并分别记录按键按下的持续时间&#xff0c;通过时间的长短…

序列化与反序列化xml bin

1、XML XML&#xff08;可扩展标记语言&#xff09;序列化是一种将对象的数据结构转换为XML格式的过程&#xff0c;以便将其存储在文件中、通过网络传输或与其他系统进行数据交换。在C#中&#xff0c;你可以使用.NET框架提供的XmlSerializer类来执行对象到XML的序列化和XML到对…

Java中访问未初始化的对象:原理、错误与防范

在Java编程中&#xff0c;访问未初始化的对象是一种常见的错误&#xff0c;可能导致程序运行时异常。这个问题的根本原因在于Java要求对象在使用之前必须经过初始化&#xff0c;否则会存在未知的状态。本文将详细讲解Java中访问未初始化对象的原理、可能导致的错误以及如何防范…

后端杂七杂八系列篇一

后端杂七杂八系列篇一 ① MySQL选择合适的数据类型① Char与Varchar② Text与Blob ② EqualsAndHashCode(callSuper true)的作用③ mybatis-plus 相关① 主键生成策略② 使用Model实现CRUD③ Wrapper的用法① Wrapper的继承关系② 项目中最常用的warpper [LambdaQueryWrapper]…

[NISACTF 2022]level-up

[NISACTF 2022]level-up wp level 1 robots.txt 通过目录爆破&#xff0c;发现存在 robots.txt 文件&#xff08;或者说查看源码&#xff0c;源码中有 disallow 提示&#xff0c;说明存在 robots.txt 文件&#xff09;&#xff1a; dirsearch -u "http://node5.anna.n…

M3u8播放列表文件(索引格式文件)、HLS(HTTP Live Streaming)协议介绍

M3U8文件格式是为了支持HLS&#xff08;HTTP Live Streaming&#xff09;协议而开发的。HLS是Apple公司设计的一种基于HTTP的流媒体传输协议&#xff0c;用于传输音频和视频数据。 在HLS中&#xff0c;媒体内容被切分为一系列的小片段&#xff0c;每个片段都有自己的URL。这些U…

Docker入门教程(详解)

Docker容器化 一 入门 1. 引言 &#xff08;1&#xff09;单机部署 场景&#xff1a; 将多个应用部署一台服务器上。 问题 每个应用软件&#xff0c;都会消耗物理资源&#xff0c;共用计算机资源&#xff0c;彼此之间会形成竞争关系。 &#xff08;2&#xff09;多机部署 …

python django 生鲜商城管理系统

python django 生鲜商城管理系统,包含用户端和管理端 功能&#xff1a; 用户端&#xff1a;商城主页展示&#xff0c;登录&#xff0c;注册&#xff0c;用户中心&#xff0c;购物车&#xff0c;我的订单&#xff0c;购物车结算 管理端&#xff1a;登录&#xff0c;商品&…

Java 堆与栈的作用与区别

栈是运行时的单位&#xff0c;而堆是存储的单位&#xff0c;栈解决程序的运行问题&#xff0c;堆解决数据存储的问题。 一个线程对应一个线程栈&#xff0c;栈是运行单位&#xff0c;里面存储的信息都是跟当前线程相关的信息&#xff0c;包括局部变量、程序运行状态、方法返回…

SpringBoot: 通过MyBatis访问ClickHouse

一、ClickHouse中建表&#xff0c;添加数据 二、SpringBoot项目添加mybatis、clickhouse、druid相关依赖 <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.6</version></dependency>…

SQL学习汇总

数据库将两张没有关联的表进行横向连接数据库将两张表进行横向连接&#xff08;拼接成一张表的形式显示&#xff09;_db2 两条记录如果相同就横向显示-CSDN博客 mysql统计某个字段的比例_mysql查询一行某个字段占整个字段sum的比例-CSDN博客 Spark 系列&#xff08;十二&…

opencv入门到精通——Canny边缘检测

目录 理论 OpenCV中的Canny Edge检测 附加资源 在本章中&#xff0c;我们将学习 Canny边缘检测的概念 OpenCV函数: cv.Canny() 理论 Canny Edge Detection是一种流行的边缘检测算法。它由John F. Canny发明 1.这是一个多阶段算法&#xff0c;我们将经历每个阶段。 2.降…

原生JS调用OpenAI GPT接口并实现ChatGPT逐字输出效果

效果&#xff1a; 猜你感兴趣&#xff1a;springbootvue实现ChatGPT逐字输出打字效果 附源码&#xff0c;也是小弟原创&#xff0c;感谢支持&#xff01; 没废话&#xff0c;上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><me…

09、docker 安装nacos并配置mysql存储配置信息

docker 安装nacos并配置mysql存储配置信息 1、docker启动nacos的各种方式2、Docker安装nacos3、MySQL中新建nacos的数据库4、挂载数据or配置目录5、运行 1、docker启动nacos的各种方式 内嵌derby数据源 docker run -d \ -e PREFER_HOST_MODEhostname \ -e SPRING_DATASOURCE_…

软件测试技术复习点

1 术语含义&#xff08;故障、错误、失效、测试用例&#xff09; 故障&#xff08;Fault&#xff09;&#xff1a;故障是软件中的静态缺陷&#xff1b; 故障屏蔽&#xff1a;软件中的某个故障可能被其他一个或多个故障屏蔽&#xff1b; 错误&#xff08;Error&#xff09;&…

1 电科院FTU检测标准学习笔记-外观检查

作者简介&#xff1a; 本人从事电力系统多年&#xff0c;岗位包含研发&#xff0c;测试&#xff0c;工程等&#xff0c;具有丰富的经验 在配电自动化验收测试以及电科院测试中&#xff0c;本人全程参与&#xff0c;积累了不少现场的经验 目录 **前言****检测大纲****外观与结构…

LeetCode 2397. 被列覆盖的最多行数,状态压缩优化回溯法

一、题目 1、题目描述 给你一个下标从 0 开始、大小为 m x n 的二进制矩阵 matrix &#xff1b;另给你一个整数 numSelect&#xff0c;表示你必须从 matrix 中选择的 不同 列的数量。 如果一行中所有的 1 都被你选中的列所覆盖&#xff0c;则认为这一行被 覆盖 了。 形式上&am…