由小程序下用户行为统计的一个问题谈起

微信小程序团队在外界高昂的呼声中,终于从小程序基础库版本 1.6.3 开始,支持简洁的组件化编程。具体使用可参考小程序开发官方文档中自定义组件介绍章节。

自从小程序退出自定义组件的能力,cluo在项目中把之前通过小程序template实现的组件逐步进行改造。自定义组件带来的好处是毋庸置疑的,比如下面这些优点:

  • 统一管理组件的dom结构、样式
  • 自封闭的代码安全性
  • 多层嵌套增强复用性的自由度
  • 团队多人协作时耦合度的大幅下降
  • ...

但是自定义组件的优劣也对于开发者本身具有较高的要求,比如组件的抽象程度(颗粒度)、组件使用时的自由度、组件使用时的便捷度、组件的扩展性、组件说明文档等等。对于一个大型的项目团队,在多人合作过程中,个人认为文档是第一重要的,没有一个详尽的设计、使用文档,对于自定义组件的维护者和使用者而言,是巨大的灾难。

而在引入自定义组件之后,初始遇到一系列问题,这里会将其中一个跟本文标题息息相关的问题着重与各位分享。先介绍一下问题的背景:

产品上线后有大量的用户行为统计需求,初始时小程序的PV、UV统计基本满足产品诉求,但随着产品迭代,对于用户实际关注的页面元素等点击行为更为关心,于是一波又一波的统计需求被提上日程。为了对用户行为(以点击为例)无差别的统计上报,组内设计了一个方案。方案如下步骤:

  • 在用户交互(点击)的模块上添加相应标识

      查看详情

  • 在顶层DOM上添加相应的事件处理器

  • 通过获取事件对象e的target的daid数据属性值,来标识用户的行为并上报到服务器
const logReportEvent = function(e) {
     // 获取 用户行为的id
     let _elemId = e.target.dataset.daid
    // 上报
    // ...
}

整个方案有2个关键的点

小程序的事件冒泡机制
操作区域的标示(daid统一覆盖)

在最外层的dom添加对tap事件的监听,凡是用户在我们关注的区域内发生点击,都可以通过e.target这个朴素的思路来获取,由于对关注区域内的daid的统一覆盖,所有上报的行为就是产品关注的部分,以此达到目标。

当项目中大量使用自定义组件后,按照原先的思路,在组件内部定义了产品关心的交互区域,但在测试中发现并没有如愿上报定义的事件id。e.target实际指向的是组件的根元素。

所以,前面铺垫如此之长,终于要引出我们要说的主题了。旧的用户行为统计方案为何在应用自定义组件后失效了呢?

各位客官,先看看C罗项目中的代码运行截图,如下所示:


由小程序下用户行为统计的一个问题谈起_第1张图片
页面代码截图

【#shadow-root】有木有很熟悉?可以看出,小程序下(至少开发者工具中)自定义组件的实现使用的是shadow dom的方式。

有些朋友对这个东西可能比较陌生,谈到影子dom就不得不提Web Components,Web components致力于在web环境下复用自定义元素,而开发者不必担心代码出现冲突,它包含4个主要技术范畴,为避免自己翻译水平有限,直接将相关概念的说明复制如下:

  • Custom elements: A set of JavaScript APIs that allow you to define custom elements and their behaviour, which can then be used as desired in your user interface.
  • Shadow DOM: A set of JavaScript APIs for attaching an encapsulated "shadow" DOM tree to an element — which is rendered separately from the main document DOM — and controlling associated functionality. In this way you can keep an element's features private, so they can be scripted and styled without the fear of collision with other parts of the document.
  • HTML templates: The