详解微信小程序(Taro)手动埋点和自动埋点的实现

每一个公司要想用户增长,都要收集和分析用户操作数据,因此埋点是必不可少的事情。
而对于前端职业发展来说,传统的手动埋点,无疑是繁琐又无聊的事情,能简化就简化。

一、手动埋点

手动埋点就是在每一处需要的地方,都加一段上报埋点的代码。影响代码的阅读体验,且散落的埋点代码不方便管理。
以页面 pv 为例,我们此前是在每一个页面中上报 pv:

// src/manual/home/index.tsx

import tracking from "./tracking";

// pageSn 是前端和产品约定的「页面在埋点系统的唯一标识」,比如这个项目首页的标识符是数字 11664
const pageSn = 11111;

export default () => {
 // useDidShow 是 Taro 专有的 Hook,等同于小程序原生 componentDidShow 生命周期,会在页面展示的时候调用。
 useDidShow(() => {
 // 通过统一封装的 sendPv 方法发送 pv 埋点
 sendPv(pageSn);
 });
 return <View>手动埋点页面</View>;
};

二、自动埋点

自动埋点可分为全自动埋点和半自动埋点。全自动埋点则是不管需不需要,将所有的点都埋了。前端肯定开心了 “以后埋点产品都不要不要找我啦”,可数据同学就哭唧唧了。

比如,腾讯和 Taro 团队共同推出 腾讯有数自动化埋点,接入超级简单。比如配置 proxyPage 为 true 即可 “上报所有页面的 browse 、leave、share 等事件”,配置 autoTrack 为 true 即可 “自动上报所有元素的 tap、change、longpress、confirm 事件”。

可从数据量和有效性来说,「全埋」等于「不埋」,因为「全埋」一方面对数据存储量要求很高,另一方面会给我们负责数据清洗的同学带来大量工作。

所以接下来,还是从中寻求平衡,着重看半自动埋点。

1、页面曝光(pv)

页面曝光(pv),理想的上报方式是:

在一个统一的地方(如 trackingConf.ts),配置好每个要埋点的页面的标识符(即 pageSn)
页面显示后,自动判断下是否需要上报(是否在 trackingConf.ts 配置文件中),要就直接上报。

具体实现

(1)统一配置埋点字段,pageSn 表示页面在埋点系统中的标识符

// trackingConf.ts
export default {
 "auto/home/index": {
 pageSn: 11111,
 },
};

当然,如果你的业务允许三七二十一,上报所有页面 pv(带上 path 让产品自己筛选),那(1)这步可以省了,直接看(2),这种方式可称为「pv 全自动埋点」。

(2)封装 usePv hook,在页面展示时,获取当前页面 pageSn、判断是否要埋 pv、要的话发送 pv

// usePv.ts

// 获取当前页面 path,借助 Taro 的 getCurrentInstance
export const getPath = () => {
 const path = Taro.getCurrentInstance().router?.path || "";
 // 去掉开头的 /,比如将 '/auto/home/index' 改为 'auto/home/index'
 return path.match(/^\/*/) ? path.replace(/^\/*/, "") : path;
};

// 获取当前页面 pageSn、判断是否要埋 pv、要的话发送 pv
// 入参 getExtra 支持携带额外参数
const usePv = ({
 getExtra,
}: {
 getExtra?: () => any;
} = {}) => {
 // 页面曝光
 useDidShow(() => {
 const currentPath = getPath();
 // 从 trackingConf 中获取 pageSn
 const pageSn = trackingConf[currentPath]?.pageSn;
 console.log("自动获取 pageSn", currentPath, pageSn);
 if (pageSn) {
  const extra = getExtra?.();
  // 通过统一封装的 sendPv 方法发送 pv 埋点
  extra ? sendPv(pageSn, extra) : sendPv(pageSn);
 }
 });
};

(3)然后封装页面组件 WrapPage ,使用上述的 usePv():

import React from "react";
import { View } from "@tarojs/components";
import usePv from "./usePv";

function WrapPage(Comp) {
 return function MyPage(props) {
 usePv();
 return (
  <View>
  <Comp {...props} />
  </View>
 );
 };
}

export default WrapPage;

(4)最后在所有页面组件,包一层 WrapPage 即可实现「所有页面按需埋点」:

// src/auto/home/index.tsx

const Index = WrapPage(() => {
 return <View>自动埋点页面</View>;
});

后续新开发一个页面,除了用 WrapPage 包裹外,只需要在第(1)步的 trackingConf.ts 中增加该页面的 pageSn 即可。

提问环节

好奇宝宝们可能要问了:

(1)WrapPage 里这样封装了 usePv(),应该如何支持上报自定义字段呢?
举个例子,产品希望 src/auto/home/index.tsx 这个页面上报 pv 的时候,额外上报一下 当前页面 URL 查询参数即 params。
很简单,就是这个页面不要用 WrapPage 包裹,而是拿到 params 后直接调用 usePv 函数:

// src/auto/home/index.tsx

const Index = () => {
 usePv({
 getExtra: () => {
  const params = Taro.getCurrentInstance().router?.params;
  return { params };
 },
 });
 return <View>自动埋点页面</View>;
});

(2)这里每个页面组件,都要用 WrapPage 包裹一下,对业务还是有侵入型了,原生小程序可以改写 Page,在 Page 中直接 usePv()。Taro 项目应该也可以这么做,实现 0 业务侵入吧?

Taro 项目中,确实可以也可以和原生小程序一样,在 App 中统一拦截原生 Page,但这样的话,上面「某些页面要计算额外参数并上报」就不好解决了。

2、页面分享

微信小程序中,存在两种分享:

  • 分享给好友:useShareAppMessage。
  • 分享到朋友圈:useShareTimeline。小程序基础库 v2.11.3 开始支持,目前只在 Android 平台可用。

具体实现

以 useShareAppMessage 为例(useShareTimeline 同理):
(1)仍在 trackingConf.ts 统一配置文件中,增加分享埋点的标识字段 eleSn (及额外参数)

// trackingConf.ts
export default {
 "auto/home/index": {
 pageSn: 11111,
 shareMessage: { eleSn: 2222, destination: 0 }, // 增加 shareMessage 包含分享好友的 eleSn、业务额外参数 destination
 }
};

(2)封装 useShareAppMessage 方法,业务调用 Taro.useShareAppMessage 的地方全局替换为这个 useShareAppMessage。

// 分享给好友,统一埋点
export const useShareAppMessage = (
 callback: (payload: ShareAppMessageObject) => ShareAppMessageReturn
) => {
 let newCallback = (payload: ShareAppMessageObject) => {
 const result = callback(payload)

 const currentPath = getPath(); // getPath 获取当前页面路径,可参考「1、页面曝光(pv)」中的 getPath
 // 从 trackingConf 中获取 pageSn、shareMessage 等
 const { pageSn, shareMessage } = trackingConf[currentPath]
 const { eleSn, ...extra } = shareMessage || {}
 let page_el_sn = eleSn
 const { imageUrl: image_url, path: share_url } = result
 const { from: from_ele } = payload

 const reportInfo = {
  from_ele,
  share_to: 'friend', // 'friend' 表示分享给好友
  image_url,
  share_url,
  ...extra
 }
 console.log('...useShareAppMessage tracking', { pageSn, page_el_sn, reportInfo })
 sendImpr(pageSn, page_el_sn, reportInfo) // 可自行封装 sendImpr 方法,发送分享埋点信息
 return result
 }
 Taro.useShareAppMessage(newCallback)
}

这样,如果有个页面需增加分享好友的埋点,直接在 trackingConf.ts 中增加 shareMessage 的 eleSn 即可,useShareTimeline 同理。

提问环节

好奇宝宝们可能要问了:页面需要增加分享好友/朋友圈的埋点,可否 0 配置(即不用修改上述的  trackingConf.ts 文件)?
与前文中「pv 全自动埋点」类似,只要和产品约定好捞数据的方式也可以,比如笔者和产品约定了:
每个页面分享好友/朋友圈,eleSn 都是 444444,然后产品通过 pageSn 判断是哪个页面,通过 share_to 判断是分享好友 / 朋友圈,对于分享好友的场景,再通过 from_ele 判断通过右上角分享还是点击页面中的按钮分享。
这样页面分享也可以全自动埋点了。

3、元素埋点

元素自动埋点的调研遇到阻力,尚未落地。下文主要谈不同思路遇到的问题,有好的建议欢迎评论区沟通。

我们元素埋点,较高频的有曝光、点击事件,中低频的有滚动、悬停等事件。

手动埋点的方式就是在元素指定事件触发的时候,手动执行 sendImpr 上报埋点(带上页面唯一标识符 pageSn、 元素唯一标识符 eleSn)。

那这个环节是否可以省事一些呢?对业务无侵入,大概的做法还是:

在 Component 指定事件触发增加个 hook -> 判断是否要上报埋点 -> 满足条件则上报

问题一分为二:

(1)拦截元素事件回调

可以拦截并遍历小程序 Component 接收到的 options.methods,如果是一个自定义函数,则在函数被调用的时候判断第一个参数(假设命名为 e)的 type 是否等于 tap 等事件。这时候可以根据 e 等信息决定是否满足埋点上报条件了。
原生小程序中的实现,大致如下:

// App.js
App({
 onLaunch() {
 let old = Component
 Component = function(config) {
  // 拦截业务传入的 config
  const newConf = proxyConfig(config)
  old(newConf)
 }
 }
})

const proxyConfig = function(conf) {
 const methods = conf.methods
 // 获取自定义方法(按需排除一些不埋点的方法)
 let diyMethods = Object.entries(methods).filter(function (method) {
 let methodName = method[0]
 return ![
  "onLoad",
  "onShow",
  "onReady",
  "onHide",
  "onUnload",
  "onPullDownRefresh",
  "onReachBottom",
  "onPageScroll",
  "onShareAppMessage",
  "onResize",
  "onTabItemTap",
  "observer",
 ].includes(methodName);
 })
 diyMethods.forEach(function(method) {
 const [methodName, methodFn] = method
 // 修改 conf 中的 methods
 methods[methodName] = function (...args) {
  const e = args && args[0]
  if (e && e.type === 'tap') {
  console.log('...tapping', methodName, args) // 触发点击事件的时候,按需上报埋点
  }
  methodFn.call(this,...args)
 }
 });
 // 返回修改后的 conf
 return conf
}

Taro 项目中,不能直接在组件代码里用 Component,但可以迂回一些的方式实现相同目的,比如:

// myProxy.js
module.exports = (function() {
 let OriginPage = Page
 let OriginComponent = Component

 return (Page = function(conf) {
 conf.forEach(function(e) {
  let [methodName, methodFn] = e

  if (typeof methodFn === 'function') {
  conf[methodName] = function(...args) {
   // 做你想做的事,如改写 conf 等
   methodFn.call(this, ...args)
  }
  }
 })
 return OriginPage(conf)
 })(
 (Component = function(conf) {
  const methods = conf.methods
  methods.forEach(function(e) {
  // 做你想做的事,如改写 conf 等
  })

  OriginComponent(conf)
 })
 )
})()

然后在 app.tsx 中直接引入 myProxy.js 即可

(2)如何自动生成元素唯一标识符

目前是通过埋点系统中申请下来的 eleSn 来唯一标识元素的,如果想要自动标识,可细分为:

  • XPath:在 pc / mobile 中还可以,但在小程序中不支持直接获取节点的 XPath / 根据 XPath 获取节点。微信小程序可否支持通过 XPath 获取 DOM 元素?
  • 自动获取 组件方法名:原生小程序中,因为直接拦截了 Component options 中的 methods,所以在事件触发时可以获取到原始的方法名,但 Taro 项目中不行,因为 methods 被代理了一道,事件触发后,你看到的方法名都是 eh。
  • AST 解析源码分析出页面名、方法名和方法对应的注释来标识元素:Taro 项目中目测只能用这个方法,但成本较大,且「在代码不断迭代后,存量数据是否还能用」也是个问题,所以笔者未做尝试。

三、总结

本文概述了一下微信小程序(Taro)从手动埋点到自动埋点的思路。并按照页面埋点(pv、分享)以及元素埋点,分析了实现方式:

  • 页面 pv:

    • 封装 usePv,根据当前页面 path 从配置文件中读取出 pageSn
    • 封装页面组件 WrapPage 调用 usePv()
  • 分享好友/朋友圈:自定义 useShareAppMessage、useShareTimeline,根据当前页面 path 从配置文件中读取出 pageSn 和分享 eleSn,然后获取传入参数后埋点上报
  • 元素埋点:提供了改写 Component 方法来拦截事件回调的思路,但因元素唯一标识符不能自动获取,所以不大适合自动化埋点。

到此这篇关于详解微信小程序(Taro)手动埋点和自动埋点的实现的文章就介绍到这了,更多相关小程序手动埋点和自动埋点内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈微信小程序列表埋点曝光指南

    背景 最近项目中,开发一个小程序列表页,PM大大给我提了一个埋点需求,列表中的每一项,出现在屏幕中的时候,需要上报一条记录. 目标 如图中操作,要依次上报 item 1 - item7,但是上滑的时候,不再重复上报 怎么做 接到需求的第一感觉是,我难道要监听页面滚动,然后通过offsetTop 去算吗!!!??? 这样显然不是最高效的.于是我去翻了一下微信的开发文档,发现有一个IntersectionObserver 的API,正好满足我的需求. 首先我们需要创建一个 IntersectionO

  • 小程序从手动埋点到自动埋点的实现方法

    前言 小程序由于封闭性较强,要像web应用一样实现灵活的数据收集,会有一定难度.目前开源的埋点SDK,一般采用手动埋点的方式,这种方式有较强的侵入型,为了解决这个问题就有了该文章. 手动埋点 以腾讯移动分析的SDK为例,如果要记录埋点信息,只要插入一句代码即可 // 例如,记录搜索行为 search(keyword) { if (keyword) { ...业务代码 } mta.Event.stat("ico_search", {"query":keyword});

  • 详解微信小程序(Taro)手动埋点和自动埋点的实现

    每一个公司要想用户增长,都要收集和分析用户操作数据,因此埋点是必不可少的事情. 而对于前端职业发展来说,传统的手动埋点,无疑是繁琐又无聊的事情,能简化就简化. 一.手动埋点 手动埋点就是在每一处需要的地方,都加一段上报埋点的代码.影响代码的阅读体验,且散落的埋点代码不方便管理. 以页面 pv 为例,我们此前是在每一个页面中上报 pv: // src/manual/home/index.tsx import tracking from "./tracking"; // pageSn 是前

  • 详解微信小程序工程化探索之webpack实战

    前言 微信小程序因为其便捷的使用方式,以极快的速度传播开来吸引了大量的使用者.市场需求急剧增加的情况下,每家互联网企业都想一尝甜头,因此掌握小程序开发这一技术无疑是一名前端开发者不可或缺的技能.但小程序开发当中总有一些不便一直让开发者诟病不已,主要表现在: 初期缺乏方便的npm包管理机制(现阶段确实可以使用npm包,但是操作确实不便) 不能使用预编译语言处理样式 无法通过脚本命令切换不同的开发环境,需手动修改对应环境所需配置(常规项目至少具备开发与生产环境) 无法将规范检查工具结合到项目工程中(

  • 详解微信小程序 同步异步解决办法

    详解微信小程序 同步异步解决办法 小程序中函数体还没有完成,下一个函数就开始执行了,而且两个函数之间需要传参.那是因为微信小程序函数是异步执行的.但微信小程序增加了ES6的promise特性支持,微信小程序新版本中移除了promise的支持,需要自己使用第三方库来自行实现ES6的promise特性. WxService.js import Tools from 'Tools' import es6 from '../assets/plugins/es6-promise' class Servic

  • 详解微信小程序Radio选中样式切换

    详解微信小程序Radio选中样式切换 本篇文章主要讲解在微信小程序中如何根据Radio选中来切换样式.效果如下: 原理主要是通过判断一个radio-group中哪个被选中,就让它加上一个"active"的样式. 代码如下: <!--index.wxml--> <view class="container"> <radio-group bindchange="radioCheckedChange"> <vi

  • 详解微信小程序 登录获取unionid

    详解微信小程序 登录获取unionid 首先公司开发了小程序, 公众号网页和app等, 之前都是用的openid来区分用户, 但openid只能标识用户在当前小程序或公众号里唯一, 我们希望用户可以在公司各个产品(比如公众号, 小程序, app里的微信登录)之间, 可以保持用户的唯一性, 还好微信给出了unionid. 下面分两步介绍一下 微信小程序 获取unionid的过程. 1. 首先 在微信公众平台注册小程序 , 然后在小程序上模拟登录流程. 注 : 这里只是简单登录流程, 实际中需要维护

  • 详解微信小程序 通过控制CSS实现view隐藏与显示

    详解微信小程序 通过控制CSS实现view隐藏与显示 实现效果图: 视图代码,使用变量控制隐藏类名 <scroll-view scroll-y="true" > <view class="user_freeback"> <view class="txt"> <text> 为了更好地帮助您解决问题,请准确填写您的邮箱地址和电话号码,以便管理员给你答复.</text> </view&g

  • 详解微信小程序 template添加绑定事件

    详解微信小程序 template添加绑定事件 对于模板的使用,我是想将模板的事件单独出来,其他引用模板的页面中不再掺杂模板事件,比较方便管理,如果还有其他好的解决办法, 请赐教. template.wxml <view bindtap="clickView" class="tempClass">temp模板</view> template.js var temp = { clickView: function () { console.log

  • 详解微信小程序设置底部导航栏目方法

    详解微信小程序设置底部导航栏目方法 小程序底部想要有一个漂亮的导航栏目,不知道怎么制作,于是百度找到了本篇文章,分享给大家. 好了 小程序的头部标题 设置好了,我们来说说底部导航栏是如何实现的. 我们先来看个效果图 这里,我们添加了三个导航图标,因为我们有三个页面,小程序最多能加5个. 那他们是怎么出现怎么着色的呢?两步就搞定! 1. 图标准备 阿里图标库  http://www.iconfont.cn/collections/show/29 我们进入该网站,鼠标滑到一个喜欢的图标上面  点击下

  • 详解微信小程序中的页面代码中的模板的封装

    详解微信小程序中的页面代码中的模板的封装 最近在进行微信小程序中的页面开发,其实在c++或者说是js中都会出现这种情况,就是相同的代码会反复出现,这就是进行一定的封装,封装的好处就是可以是程序中在于减少一定的代码量,并且可是使代码结构更加清晰.那今天所要记录的就是关于微信小程序中的页面的模板封装. 在微信小程序中的文件名都带有wxml等样式,在wxml中提供了模板,即可以在模板中定义代码片段,然后可以在页面中的不同位置进行调用,模板的定义: <templatename="products&

  • 详解微信小程序 相对定位和绝对定位

    详解微信小程序 相对定位和绝对定位 相对定位:元素是相对自身进行定位,参照物是自己. 绝对定位:元素是相对离它最近的一个已定位的父级元素进行定位 相对定位: position:relative;    /*启用相对定位*/         left:150rpx;    /*相对于自己往右偏离150*/         top:50rpx;     /*相对于自己往下偏离150*/ 绝对定位: position: absolute;           left: 50rpx;         

随机推荐