前言
产品:明明
啊,页面A的按钮能不能给我加个点击的埋点。页面B的 banner 访问埋点加下。
过了 n 天,产品: 明明
啊, 页面 A 的点击次数好像和页面 B 的访问次数对不上,能不能再给页面 A 和 B 都加个页面访问的埋点。
明明: 好的没问题。
可视化埋点
可视化埋点解决了哪些问题
- 快速添加埋点事件,无需发布代码,线上埋点立即生效。
- 支持点击事件、元素浏览埋点事件类型,满足日常埋点需求。
- 支持自定义事件参数定义,区分埋点事件来源。
- 从此告别产品琐碎的埋点需求,您自个玩嘞😄。
可视化埋点三大模块
结合文章最开始的图来看,可视化埋点分为以下三个部分:
- sdk 区分
iframe mode
、debug mode
、user mode
进行各自场景下的交互。 - socket 服务 作为
iframe mode
和debug mode
模式下的通信桥梁。 - 可视化后台,和
iframe mode
下嵌入的页面进行交互,可视化圈选需要埋点的 dom 。
可视化埋点流程分析
sdk 如何区分环境
iframe mode
、debug 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
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 实例,我们需要将它们存储起来。
class IoServer{
users: User[] = [] // 在线用户列表
constructor(io) {
io.on("connection", (socket) => {
// 每次重新连接接受一个新的 socket实例
this.users.push(new User(socket))
})
}
}
如何关联 client socket 和 server socket
当我们有多个 client 连接后, debug mode
和 iframe mode
之间如何一对一进行通信呢?
当我们有多个客户端连接后,假设 iframe1 想要将一个消息推送给 debug1 该如何操作?
- 图中有四个客户端连接 socket 服务器,创建了四个 socket 实例。
- 当 iframe1
socket.emit('custom-event')
事件给服务端 - 假设服务端的 socket 都注册了该事件
socket.on('custom-event', listener)
就会执行 listener 监听函数。 - 我们需要找到 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 mode
和 debug 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 页面需要和可视化后台页面进行通信。
iframe 向后台页面发送消息
- 当 socket 连接成功
- 当移动端页面跳转
后台向 iframe 发送消息
- 当添加、删除、修改埋点
- 请求埋点初始化
// iframe
window.parent.postMessage(params, '*')
window.addEventListener('message',listener)
// 后台
iframe.contentWindow.postMessage(params, '*')
window.addEventListener('message',listener)
如何进行埋点上报
在可视化埋点后,会将埋点 DOM 对应的 XPath 等信息进行存储。当处于 user mode
- 请求服务端获取配置接口
- 当触发 click、 browse 事件时候会进行比对是否要上传埋点
- 如果事件触发时候配置文件未请求完毕,会对事件数据进行缓存,当配置文件获取后再进行比对和清空。
后序
产品: 明明
我有个新上的页面需要加下埋点呀,你有没有时间的?
明明: 用可视化埋点呀