最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 微信小程序(Taro)手动埋点和自动埋点

    正文概述 掘金(燕行者)   2021-03-02   746

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

    一、手动埋点

    手动埋点就是在每一处需要的地方,都加一段上报埋点的代码。影响代码的阅读体验,且散落的埋点代码不方便管理。

    以页面 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,
      },
    };
    

    (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)手动埋点和自动埋点

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元