Intersection Observer API: https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API
2023.2.20 星期一
小程序,h5,taro等埋点:属性埋点,自动/全/零埋点,区别于手动上报。
曝光埋点
IntersectionObserver 方式
1 | var intersectionObserver = new IntersectionObserver(entries => { |
主要用到的APIIntersectionObserver
主要用来检测被监听的目标元素可见部分与root元素的交叉状况,比如获取相交区域的比例值,后面做曝光埋点的判断需要用到。
requestIdleCallback
方法,浏览器会在空闲时执行传入的函数。后面埋点我们使用这个方法,避免埋点影响主业务。
react 两种方式
实现的方式主要有两种:
- 函数的方式;
- 高阶组件的方式;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// # 调用方式
// ## 函数方式
// 重新设置IntersectionObserver的配置
exposeListener(document.querySelector('.dom3'), {
observerOptions: {
threshold: [0, 0.2, 1],
},
onExpose() {
console.log('dom1 expose', Date.now());
},
});
// ## 类的方式
// ## useEffect()
<ComExpose always onExpose={() => console.log('expose')} onHide={() => console.log('hide')}>
<div className="dom dom1">dom1 always</div>
</ComExpose>
PS: 可以用自定义useExpo。
<!– PS:
app.jsx中使用新的hooks;不用每个组件都调用。可以用高阶组件, 或者函数试:useEffect(() => {useExpo()}, [])
高阶组件,不用通过class获取目标元素。而是const ref = useRef<any>(null);
–>
Taro
taro或者小程序项目有api:Taro.createIntersectionObserverconst observer = Taro.createIntersectionObserver(this, { thresholds: [0], observeAll: true })
创建并返回一个 IntersectionObserver 对象实例。
监听文档/dom的变化以他用
refactor(runtime/shared/api): 优化获取节点的逻辑,增加其成功率
- 小程序和 H5 的 createIntersectionObserver 和 createSelectorQuery API 自动在一个 Taro.nextTick 后执行,减少开发者手动使用 Taro.nextTick 或 setTimeout 的需要。
- Taro.nextTick 增加等待执行逻辑,增加其在开发框架完成渲染后再执行的成功率
SMTC
兼容h5语法,使用原生js写法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63// useExposure。
useEffect(() => {
const observeCb = (e) => {
// const dataset = document.getElementById(res.id)?.dataset;
let target;
let dataset;
// let intersectionRatio:number;
if (process.env.TARO_ENV === 'h5') {
target = e.target;
dataset = target.dataset;
} else {
dataset = document.getElementById(e.id)?.dataset
}
if (!dataset) return;
// do sth
}
if (process.env.TARO_ENV === "weapp") {
timerRef.current = setTimeout(() => {
obRef.current = Taro.createIntersectionObserver(this as unknown as TaroGeneral.IAnyObject, {
observeAll: true
});
obRef.current
.relativeToViewport({ top: 10 })
.observe(".expo", observeCb);
}, 0)
} else {
const options = {
root: null,
// rootMargin: '0px 0px 0px 0px',
// threshold: [0, 0.3, 1],
};
const observer = new IntersectionObserver((entries/* , observer */) => {
entries.forEach(requestIdle(observeCb));
// entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
// const target = entry.target;
// observeCb(entry)
// });
}, options);
console.log("[Expo]doms:", document.querySelectorAll('.expo'))
// eslint-disable-next-line semi-style
;[].slice.call(document.querySelectorAll('.expo')).forEach(el => {
observer.observe(el);
});
// window['myObserver'] = observer;
obRef.current = observer;
}
return (() => {
obRef.current?.disconnect()
timerRef.current && clearTimeout(timerRef.current);
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
滚动监听
点击埋点
自动埋点
web页面
document.addEventListener
taro 小程序
运行时注入事件监听modifyDispatchEvent
。
Taro小程序-生命周期
思路
我们要想要实现无侵入或低侵入的监控页面声明周期函数,对于原生的微信小程序,我们可以参考网上的一个现有解决方案:小程序从手动埋点到自动埋点。
其实现原理主要是:通过代理微信小程序的Page方法,在用户传递进来的生命周期钩子函数外层包装一层wrapper函数,并在wrapper函数中实现统一数据上报的逻辑,然后再调用用户定义的声明周期钩子函数,这样,使用者便可以在无感知的情况下进行编码,所有的数据收集与上报操作都可以在这个wrapper函数中执行。
然而,上述方案仅适用于原生微信小程序,在基于Taro开发的微信小程序项目中,由于在Taro中所有单元都是组件Component,而非Page,经过本人的反复试验,Taro在运行的过程中,并没有调用过Page方法,因此,通过代理微信原生Page方法这条路是行不通了。
那么,既然在Taro中一切皆组件,我们能不能通过代理Component实现类似的逻辑呢?经过试验,这个想法是可行的,不过由于Component的生命周期钩子跟Page的生命周期钩子不一样,所以我们需要对其做一定的转化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/**
* 重写微信原生Page
* @param newPage
*/
export function overrideWxPage(newPage: any):void {
Page = newPage;
}
// 需要代理的生命周期钩子,包含Page和Component的钩子
const proxyMethods = [
"onShow",
"onHide",
"onReady",
"onLoad",
"onUnload",
"created",
"attached",
"ready",
"moved",
"detached",
];
Github: taro-track
注:由于taro是第三方框架,版本升级不可控,因此,我们只是对特定个版本,如2.1.5进行兼容,其他版本尚未做兼容处理
<!–
了解微信小程序 Taro 的自动埋点
POST: 2020-09-10 17:12:43
而在小程序中因为其和浏览器不同的架构,导致了监听页面变的更加困难,通常我们都会通过重写 Page 方法来达到对小程序原生生命周期的拦截代理,从而进行业务埋点,但是在 Taro 中这一切变得不同了。
想在小程序中进行自动的埋点,其实要做的就是在小程序指定的生命周期里做一些固定的处理,所以我们自动埋点的问题实际上是如何劫持小程序的生命周期,而要劫持小程序的生命周期,我们需要做的就是去重写options。
–>
weapp-lifecycle-hook-plugin: npm插件。
PS: 以上两篇基于taro2.x。
Taro小程序-事件
# 30-a Taro3 无埋点的探索与实践
这点在 Taro2 时期已经是实现完美适配的,但在 Taro3 之后,由于 Taro 团队对其整体架构的调整,使得之前的方式已经无法实现准确的无埋点,促使了本次探索。
<!–
GrowingIO 小程序 SDK 无埋点功能的实现有两个核心问题:
- 如何拦截到用户事件的触发方法
- 如何为节点生成一个唯一且稳定的标识符
如果想处理掉已定义无埋点事件失效问题,那就必须能提供一个稳定的标识符。类比与在 Taro2 上的实现,如果也能在拦截到事件触发的时候获取到用户方法名,那就可以了。也就是说只要能把以下两个问题处理掉,便能实现这个目标了。
- 运行时 SDK 能拦截用户方法
- 能在生产环境将用户方法名保留下来
结语
–>
在 Taro3 无埋点功能的实现上,GrowingIO 小程序 SDK 从运行期和编译期同时下手,在运行期实现事件拦截,在编译期实现用户方法名的保留,以此实现较稳定的无埋点功能。具体的使用方式可见:Taro3中集成GrowingIO小程序SDK。通过这次 Taro3 无埋点的支持,GrowingIO 小程序无埋点实现也从仅运行期的操作扩展到了编译期,这也是一种新的方式,未来也可能会在这个方向上继续优化,提供更稳定的无埋点功能。相关 Babel 插件以开源,仓库可见:
growing-babel-plugin-setname
npm install --dev babel-plugin-setname
为匿名函数设置函数名
平台
Taro 引入了腾讯有数的微信小程序无痕埋点能力,为 Taro 的开发者提供真·零开发的 8 大无痕埋点能力以及自定义埋点能力,包含小程序启动、显示、隐藏、页面浏览、页面离开、分享、下拉刷新、上拉触底等八大自动化埋点能力以及搜索、商品归因等定制化埋点,以及经营分析、直播分析、导购分析等能力,让你的小程序可以基于微信生态,串联全场景多触点,实现全域经营洞察。