共计 4188 个字符,预计需要花费 11 分钟才能阅读完成。
公司零碎需要加上埋点性能,用来统计各页面性能的应用状况。于是,联合网上材料以及之前应用埋点零碎的经验,认真钻研钻研。
调研
埋点分类
常见的埋点类型有三种
-
代码埋点
- 通过 JavaScript 代码被动将所须要的信息上报给服务器。
- 长处:能够准确的上报所需的数据,对于大量埋点需要较为适合。
- 毛病:代码遍布我的项目各处,不好保护治理。且埋点只能通过开发人员手动实现。
-
可视化埋点
- 须要另外一个可视化埋点圈选零碎来圈选须要埋点的 DOM 元素。而后通过在零碎中集成 SDK 来被动上报这些区域的埋点信息。其实算是另一种意义上的代码埋点。
- 长处:有圈选零碎能够让产品、运维同学自行决定埋点区域。
- 毛病:适用范围无限,如内网零碎、挪动端 Hybrid 页面这些就很难用内部的可视化埋点来做。
-
无埋点
- 其实也叫全量埋点,即全局监听系统事件,把用户所有行为都进行上报。
- 长处:行为数据记录全面,无需减少或保护埋点代码。
- 毛病:上报数据量大,对服务器有肯定压力。且无奈准确上报某一性能的特停数据。
埋点指标
- 数据监控:通过埋点让产品运维同学晓得我的项目以后的具体情况,从而有针对性的去优化我的项目。
- 异样监控:从开发角度去收集我的项目中产生的 JS 报错、接口报错等异常情况。发现问题、解决问题、优化我的项目。
- 性能监控:收集我的项目运行中的各种性能指标,如白屏工夫、首屏加载工夫、接口申请工夫等等。
埋点 SDK 实现猜测
以我之前工作中用到过的埋点零碎 GrowingIO 为例。咱们能够通过它的 SDK 文档 来验证下面的实践。
- 它通过全局引入 JS 代码的形式来进行集成,它会在 window 全局对象下加上一个 gio 函数解决各种埋点行为。
- 因为埋点零碎会为很多我的项目服务,所以须要初始化的时候加上
gio('init', 'your projectId', {})
。 - 它要求在须要圈选的 DOM 元素上
data-growing-container
属性,这其实是 HTML 元素的 dataset 属性,能够用来对元素进行自定义数据属性的读写操作。有了圈选标记,埋点事件拦挡的时候就能够指哪打哪了。 - 它通过
gio('track', eventId, eventLevelVariables);
函数实现了被动埋点行为,这个天然是必不可少的。总有埋点需要是主动埋点做不了的。 - 它的无埋点记录的是所有元素的点击量和浏览量,应该是全局监听了元素的点击和可视事件。
- 它的可视化圈选是通过 XPath 来惟一定位一个元素的,那么可视化圈选其实就是将指标 DOM 的 xPath 保存起来,在埋点的时候去获取指定 DOM 元素的点击量和访问量。(对于 xpath 的应用能够看 Introduction to using XPath in JavaScript – XPath | MDN)
我的埋点
计划抉择
因为我的项目的埋点只须要记录一些指定的行为,所以全埋点计划被我 PASS 了。同时也没有必要另外写一个页面去做埋点的圈选,最终,抉择了最简略粗犷地被动埋点。
被动埋点 1.0
一开始埋点其实很简略,通过在 JavaScript 代码中写埋点代码来进行实现。
定义一个埋点工具对象。
// logger.js
export default {
...,
track(data) {const configInfo = this.getConfigInfo() // 一些公共配置信息,如用户名、token、工夫、url 等
return fetch.post('/api/v1/web/log', {
...data,
...configInfo,
})
},
}
将 logger 对象绑到 Vue 的原型中。
// main.js
Vue.prototype.$logger = logger
在须要的中央被动埋点。
<template>
<div>
<el-button @click="download">download</el-button>
</div>
</template>
<script>
export default {
name: 'demo',
methods: {download() {window.open('file url')
this.trackLogger()},
trackLogger() {
this.$logger.track({
component_id: '2',
component_name: '下载按钮',
})
},
},
}
</script>
<style lang="scss" scoped></style>
遇到的问题
其实被动埋点应该就是如此,但随着埋点代码的逐步增多(曾经从起初的 20 条减少到 203 条了……)。看代码的时候就十分好受了。形容一个场景:
- 须要查看共事代码中的埋点状况,因为不分明他的代码,就须要一点点找了。
- 全局搜寻埋点代码
$logger.track()
,失去 n 个蕴含有埋点代码的函数。 - 再一一跟踪这些蕴含埋点代码的函数的触发地位(有时候还会是函数嵌函数),最终找到绑定函数的 DOM 元素。
- 如此才算是确定了一个元素领有埋点行为。
申明式 vs 命令式
面对下面的场景,我在想有没有方法可能省去一一查函数的步骤,让被动埋点代码更加直观呢。这里就得提到另外一个点了:申明式代码与命令式代码的区别了。
- 申明式代码:如 HTML、XML、CSS,它的特点是可读性更强,形容的时候更合乎直觉、更形象。
- 命令式代码:如 JavaScript,它的特点是更合乎行为步骤的思考模式,适宜解决一些逻辑性强的性能。
举几个栗子
比方画一幅画,用申明式的形式来形容是“我要画一幅画,它有青草、大树和天空”;而用命令式的形式形容是 ” 我要画一幅画,首先须要画青草,而后再画大树,最初加上蓝色的天空。”
还有一个例子,在 vue 中有一个 createElement
函数,它能够在 vue 的 render 函数中命令式的创立 DOM 元素。
createElement(
'anchored-heading',
{
props: {level: 1,},
},
[createElement('span', 'Hello'), 'world!'],
)
但这种命令式的写法可读性很差。vue 官网也发现了这个问题,于是引进了 JSX 来补救这个缺点。
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render: function (h) {
return (<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
},
})
JSX 的写法显著就更偏差于申明式。
那么回过来温习下被动埋点的目标:通过代码被动上报 指定 DOM 元素 的行为事件。所以个人感觉用申明式写法会更好一些。
被动埋点 2.0
说干就干,我试着将命令式埋点改为申明式埋点。
首先在入口文件 main.js
中引入全局注册逻辑。
// 事件名称
const COMPONENT_MAP = {
1: '图表切换',
2: '下载按钮',
}
// 修复点击子元素不上报埋点信息的问题
function bindDataset(el, value) {
el.dataset.loggerId = value
// 递归绑定 dataset 到所有子集上
el.children.forEach((child) => {bindDataset(child, value)
})
}
// 全局注册指令,在须要埋点的 DOM 上加上 dataset
Vue.directive('logger', {bind: function (el, binding) {const { value} = binding
bindDataset(el, value)
},
})
// 全局监听组件点击事件,退出防抖是为了防止短时间内疾速反复点击
document.addEventListener(
'click',
throttle((e) => {if (e.target.dataset.loggerId) {
this.$logger.track({
component_id: e.target.dataset.loggerId,
component_name: COMPONENT_MAP[e.target.dataset.loggerId],
})
}
}, 2000),
)
在下面代码中,我将埋点通过 vue 指令 的形式将埋点信息绑定到指标 DOM 的 dateset 下面。而后通过 全局 click 事件拦挡 来取得指标元素的点击行为,并上报埋点信息。
- 因为没有找到如何间接在 Vue 组件上间接操作 DOM 的形式(ref 不算,那个须要写很多的 ref=’xxx’ 很不划算),所以想到了 Vue 指令。
- 在点击 DOM 元素的时候,如果元素中有子节点那么全局 click 事件只能捕捉到子节点的事件,于是我偷懒将子节点都加上了 dataset。(组件的子元素不会太多,偷个懒了)
以上遇到的两个问题个人感觉不是最佳计划,如果有好的解决方案欢送探讨呀!
应用形式如下,可读性上强了不少。
<div class="filter-wrap" @click="setFilterPopupVisible(true)" v-logger="1">
<img class="filter-icon" :src="filterIconUrl" />
<img
class="filter-icon-checked"
:src="filterSelectedIconUrl"
v-show="isFilterActive"
/>
</div>
如此,当前在看埋点代码的时候只有全局搜寻 v-logger
就能够很不便的看到有哪些 DOM 元素或者 vue 组件是进行了埋点的了。不须要重复去查各种事件了。
最初
折腾了一圈,次要就是想解决看被动埋点代码太恶心的问题。而后顺便温习一些知识点。
- 申明式编程和命令式编程
- 埋点相干常识
- dataset
- xpath
参考资料
- growing IO js SDK 文档
- dataset | MDN
- Introduction to using XPath in JavaScript – XPath | MDN)
- https://juejin.cn/post/6844903650603565063
- https://juejin.cn/post/7163046672874864676
- https://juejin.cn/post/7085679511290773534
- https://time.geekbang.org/column/article/140196