如何搭建可视化埋点

2,762 阅读5分钟

可视化埋点 (1).png

前言

产品:明明啊,页面A的按钮能不能给我加个点击的埋点。页面B的 banner 访问埋点加下。

过了 n 天,产品: 明明啊, 页面 A 的点击次数好像和页面 B 的访问次数对不上,能不能再给页面 A 和 B 都加个页面访问的埋点。

明明: 好的没问题。

image.png

可视化埋点

可视化埋点解决了哪些问题

  • 快速添加埋点事件,无需发布代码,线上埋点立即生效。
  • 支持点击事件、元素浏览埋点事件类型,满足日常埋点需求。
  • 支持自定义事件参数定义,区分埋点事件来源。
  • 从此告别产品琐碎的埋点需求,您自个玩嘞😄。

可视化埋点三大模块

结合文章最开始的图来看,可视化埋点分为以下三个部分:

  • sdk 区分 iframe modedebug modeuser mode进行各自场景下的交互。
  • socket 服务 作为 iframe modedebug mode 模式下的通信桥梁。
  • 可视化后台,和 iframe mode 下嵌入的页面进行交互,可视化圈选需要埋点的 dom 。

可视化埋点流程分析

sdk 如何区分环境

iframe modedebug mode 这两个模式代表两个设备访问了同一个地址,并且它们需要连接 socket 。那么如何区分这两个模式呢?

来看下,在 iframe mode 模式下,我们是 iframe 嵌入的页面加载

/**
 * 判断页面是否嵌在 iframe 中
 * @returns {Boolean}
 */
 export function isWithInIframe () {
  return  window.self !== window.top
}

那么如何区分 debug mode 模式呢
由于移动端连接需要扫码,我们二维码链接添加额外的参数就可以区分访问的链接是 debug mode
例如我们访问的页面地址为

https://juejin.cn

我们将生成二维码的链接加上我们特有的参数。

https://juejin.cn?DEBUG_SING=1

为什么要区分环境

来看下不同环境下的页面

iframe mode Screen Shot 2022-06-07 at 4.39.15 PM.png

debug mode

可以看到在不同模式下:

  • 同一个链接访问的样式是不同的,例如:在 iframe mode 下会有绿色框表明可以圈选的 dom
  • 并且事件监听也会不同,例如: 在 iframe mode 下点击获取验证码按钮会打开埋点事件添加的摊开,而在 debug mode 下点击获取验证码会正常执行 dom 绑定的监听事件。

总结来说

  • iframe mode 是为可视化埋点添加提供 html 相关的信息,如 dom 的 XPath
  • debug mode 是为可视化埋点的真机体验,在该模式下的交互和 user mode 相同,会将 点击事件输入框事件页面跳转同步到 iframe mode的页面上。

socket连接

客户端连接

import { io } from "socket.io-client"
const socket = io(serviceUrl)
socket.on('connect', function () {
    // socket.emit(eventName)
})

服务端连接, 由于会有多个客户端连接服务端,会产生对应的 socket 实例,我们需要将它们存储起来。

image.png

class IoServer{
  users: User[] = [] // 在线用户列表
  constructor(io) {
    io.on("connection", (socket) => {
      // 每次重新连接接受一个新的 socket实例
      this.users.push(new User(socket))
    })
  }
}

如何关联 client socket 和 server socket

当我们有多个 client 连接后, debug modeiframe mode 之间如何一对一进行通信呢?

当我们有多个客户端连接后,假设 iframe1 想要将一个消息推送给 debug1 该如何操作?

image.png

  1. 图中有四个客户端连接 socket 服务器,创建了四个 socket 实例。
  2. 当 iframe1 socket.emit('custom-event') 事件给服务端
  3. 假设服务端的 socket 都注册了该事件 socket.on('custom-event', listener) 就会执行 listener 监听函数。
  4. 我们需要找到 debug1 的 socket 并把数据传送给它对应的 client socket。

我们需要如何找到 debug1 对应的服务端的 socket 呢?

我们可以添加用户的角色,将每个 socket 当作一个用户,当连接的时候创建用户。

export class User {
  sessionKey // 关联 pc 和 移动端页面
  socket 
  device // 设备类型,区分 iframe 或 debug 
  constructor(options: {sessionKey: string; socket: any, device: MessageSouce}) {
    const { sessionKey, socket, device } = options
    this.sessionKey = sessionKey 
    this.socket = socket
    this.device = device
  }
}

这里的 sessionKey 是我们 client 传递过来的。我们可以在页面访问的时候给连接中添加 sessionKey 参数,这样 iframe modedebug mode 可以通过这个参数进行设备关联。

    // 服务端判断当前连接的 socket 是否存在,不存在则添加到 users 列表中
    socket.on('device-connect', (params: BaseModel) => {
      const { source, sessionKey, operateType } = params
      /*** 连接 */
      const index = this.users.findIndex(item => item.sessionKey === sessionKey && item.device === source)
      switch(operateType) {
        case OperateType.connect :{
          if(index === -1) {
            this.users.push(new User({
              sessionKey,
              device: source,
              socket
            }))
          }
          break
        }
    }

此时如果 iframe1 想要跟 debug1 进行通信,那么可以传递 sessionKey 过去,然后 socket 服务通过它找到移动端的 socket,把信息传递给 debug1 。

总结:

  • client socket.emit('event') 会触发它对应的 service(只有一个) `socket.on('event') 反之也相同。服务端的 emit 只能触发它对应的 client 监听事件。
  • 如果服务端想通知所有客户端可以使用 io.emit('event')
  • 如果服务端想通知除了发送端的其他客户端可以使用 socket.broadcast('event')

iframe 通信

嵌入的 iframe 页面需要和可视化后台页面进行通信。

image.png iframe 向后台页面发送消息

  • 当 socket 连接成功
  • 当移动端页面跳转

后台向 iframe 发送消息

  • 当添加、删除、修改埋点
  • 请求埋点初始化
// iframe
window.parent.postMessage(params, '*')
window.addEventListener('message',listener)
// 后台
iframe.contentWindow.postMessage(params, '*')
window.addEventListener('message',listener)

如何进行埋点上报

在可视化埋点后,会将埋点 DOM 对应的 XPath 等信息进行存储。当处于 user mode

  1. 请求服务端获取配置接口
  2. 当触发 click、 browse 事件时候会进行比对是否要上传埋点
  3. 如果事件触发时候配置文件未请求完毕,会对事件数据进行缓存,当配置文件获取后再进行比对和清空。

可视化埋点,进入页面批量高亮埋点元素.png

后序

产品: 明明我有个新上的页面需要加下埋点呀,你有没有时间的?

明明: 用可视化埋点呀

image.png