评论

如何自己在小程序内做埋点数据统计

如何在小程序自己做埋点。

如何在自己小程序内做数据埋点

小程序后台已经有了较为完善的数据统计和基础的分析,但是功能还是比较基础的,通常我们对数据分析有较高的要求时,就需要自己做数据收集了。那小程序内如何做自动的数据埋点和手动埋点呢。

自动埋点

  • 启动时间 (onLaunch)
  • 系统信息 (systemInfo)
  • 停留时长 (Page onHide - Page onShow)
  • 来源 (query 参数)
  • PV (Page onShow)
  • UV (结合用户筛选PV)
  • 预设点击数据收集 (onTap 等)

手动埋点

  • 自定义点击收集数据

改造小程序生命周期

自动埋点是需要集成到底层内,不能对业务进行侵入,所以,我们需要改造小程序生命周期,在不同的生命周期内进行预设收集数据的功能。

  • App 进行重写
const oldApp = App;
// 我们需要重写的方法
const appFn = ['onLaunch', 'onShow', 'onHide'];
App = function (options) {
  let oldFuncs = {};
  appFn.forEach((item) => {
    oldFuncs[item] = options[item]
  })
  appFn.forEach((item) => {
    options[item] = function (options) {
      // todo 做各类数据收集
      oldFuncs[item].apply(this, arguments)
    }
  })
  oldApp.apply(this, arguments);
};
  • Page 重写
const oldPage = Page;
const pageFn = ['onLoad', 'onShow', 'onHide', 'onUnload', 'onShareAppMessage', 'onAddToFavorites']

Page = function (options) {
  let oldFuncs = {};
  pageFn.forEach((item) => {
    if (options[item]) {
      oldFuncs[item] = options[item]
    }
  })
  pageFn.forEach((item) => {
    if (options[item]) {
      options[item] = function () {
        console.log('Page', item, ); // 收集各类数据
        oldFuncs[item].apply(this, arguments)
      }
    }
  })
 // 以下代码则是对除生命周期类的方法进行重写,做预设点击事件收集数据 
  const methods = getMethods(options);

  if (!!methods) {
    for (var i = 0, len = methods.length; i < len; i++) {
      clickProxy(options, methods[i]);
    }
  }
  oldPage.apply(this, arguments);
}
  • Component 重写
const oldComponent = Component;
Component = function (options) {
  // 对组建内 methods 进行重写预设点击事件埋点收集
  Object.keys(options.methods).forEach((method) => {
    clickProxy(options.methods, method)
  })
  oldComponent.apply(this, arguments);
}

以上对 App Page Component 进行重写之后,在必要的地方,加入自己的上报代码.

以下完整代码


const mpHook = {
  data: 1,
  onLoad: 1,
  onShow: 1,
  onReady: 1,
  onPullDownRefresh: 1,
  onReachBottom: 1,
  onShareAppMessage: 1,
  onPageScroll: 1,
  onResize: 1,
  onTabItemTap: 1,
  onHide: 1,
  onUnload: 1,
};
const oldApp = App;
const oldPage = Page;
const oldComponent = Component;

const appFn = ['onLaunch', 'onShow', 'onHide']

App = function (options) {
  let oldFuncs = {};
  appFn.forEach((item) => {
    oldFuncs[item] = options[item]
  })
  appFn.forEach((item) => {
    options[item] = function (options) {
      console.log('App', item); // 收集各类数据
      oldFuncs[item].apply(this, arguments)
    }
  })
  oldApp.apply(this, arguments);
};

const pageFn = ['onLoad', 'onShow', 'onHide', 'onUnload', 'onShareAppMessage', 'onAddToFavorites']

Page = function (options) {
  let oldFuncs = {};
  pageFn.forEach((item) => {
    if (options[item]) {
      oldFuncs[item] = options[item]
    }
  })
  pageFn.forEach((item) => {
    if (options[item]) {
      options[item] = function () {
        console.log('Page', item, ); // 收集各类数据
        oldFuncs[item].apply(this, arguments)
      }
    }
  })

  const methods = getMethods(options);

  if (!!methods) {
    for (var i = 0, len = methods.length; i < len; i++) {
      clickProxy(options, methods[i]);
    }
  }
  oldPage.apply(this, arguments);
}
Component = function (options) {
  Object.keys(options.methods).forEach((method) => {
    clickProxy(options.methods, method)
  })
  oldComponent.apply(this, arguments);
}

function clickProxy(options, method) {
  const oldFunc = options[method];
  options[method] = function () {
    const pages = getCurrentPages();
    const currentPage = pages[pages.length - 1];
    const pageQuery = currentPage.options || {};
    const pagePath = currentPage.route;
    const res = oldFunc.apply(this, arguments);
    let prop = {},
      type = "";
    if (isObject(arguments[0])) {
      const current_target = arguments[0].currentTarget || {};
      const dataset = current_target.dataset || {};
      type = arguments[0]["type"];
      prop["$event_type"] = type;
      prop["$event_timestamp"] = Date.now();
      prop["$element_id"] = current_target.id;
      prop["$element_type"] = dataset["type"];
      prop["$element_content"] = dataset["content"];
      prop["$element_name"] = dataset["name"];
      prop["$page_path"] = pagePath;
      prop["$page_quey"] = pageQuery;
      if (isObject(arguments[0].event_prop)) {
        prop = Object.assign(prop, arguments[0].event_prop);
      }
    }
    console.log('type', type)
    if (type) { //  可以对不同事件类型进行筛选是否需要收集
      post(prop)
    }
    console.log(res);
    return res;
  };
}

const getMethods = function (options) {
  let methods = [];
  for (let m in options) {
    if (typeof options[m] === "function" && !mpHook[m]) {
      methods.push(m);
    }
  }
  return methods;
};

const isObject = function (obj) {
  if (obj === undefined || obj === null) {
    return false;
  } else {
    return toString.call(obj) == "[object Object]";
  }
};

const post = function (data) {
  console.log('data', data)

  // 提交数据时,可以在组合下 systeminfo 用户信息等相关信息
  wx.request({
    url: 'https://www.example.php',
    method: 'post',
    data
  })
}

// 手动埋点的部分,自己在需要收集的地方调用相关方法收集数据

最后一次编辑于  2021-06-24  
点赞 1
收藏
评论

3 个评论

  • 光与风的颂
    光与风的颂
    10-20

    Component重写后,自定义组件里的点击事件可以上报了。但是Page页面里的的view的点击事件怎么办呢?没有处理吗?

    10-20
    赞同
    回复
  • M
    M
    04-16

    怎么使用这个埋点方法呢,

    04-16
    赞同
    回复
  • Captain Bond
    Captain Bond
    2021-10-18

    支持对小程序直播互动行为做监测吗

    2021-10-18
    赞同
    回复
登录 后发表内容