小程式從手動埋點到自動埋點

蟹老闆愛寫程式碼發表於2019-01-23

前言

小程式由於封閉性較強,要像web應用一樣實現靈活的資料收集,會有一定難度。目前開源的埋點SDK,一般採用手動埋點的方式,這種方式有較強的侵入型,同時開發成本較高,為了解決這個問題就有了該文章。

手動埋點

以騰訊移動分析的SDK為例,如果要記錄埋點資訊,只要插入一句程式碼即可

// 例如,記錄搜尋行為
search(keyword) {
   if (keyword) {
       ...業務程式碼
   }
   // 埋點程式碼
   mta.Event.stat("ico_search", {"query":keyword});
}
複製程式碼

示例程式碼看起來是比較簡潔的,但是埋點需要收集的資料往往不是單一的,複雜的埋點程式碼插入業務程式碼,會影響程式碼的閱讀體驗,而且埋點程式碼散落在各個地方,不方便管理

由於手動埋點必須插入到函式中,有時候我們為了獲取頁面某一元素點選資訊,產生了一種叫無業務相關埋點,簡單來說就是你的函式定義,就只有埋點程式碼,當這種埋點頻繁出現,程式碼會被嚴重汙染

// wxml
<view bindtap="track">這只是一個展示view</view>

//js 
track() {
  mta.Event.stat("eleClick", {"name":xxxxx});
}
複製程式碼

另外,由於PM會頻繁調整埋點資訊,而埋點是一個繁瑣又無聊的工作,基於Don't Repeat Yourself 原則,手動埋帶要不得。

總結以上,手動埋點有下列問題

  1. 影響程式碼的閱讀體驗
  2. 埋點程式碼散落在各個地方,不方便管理
  3. 程式碼會被汙染
  4. 埋點是一個繁瑣又無聊的工作

自動埋點

1、通過事件冒泡監聽元素是否被點選

小程式沒有提供Dom的事件監聽方法,不過我們可以通過事件冒泡的方式監聽,在wxml最外層繫結catchtap事件獲取點選元素的座標,判斷點選元素與監聽目標的座標是否相交觸發記錄。

判斷點選位置與元素是否相交方法:

小程式從手動埋點到自動埋點

下面為實現程式碼

// 小程式監聽頁面點選,使用者的點選行為都會執行elementTracker方法
<view catchtap='elementTracker'>
  <view class='buy-now'>
     <button bindtap='buy'>立即購票</button>
  </view>
</view>

// js 
elementTracker(clickInfo) {
  // 需要記錄元素的className
  const trackElementName = '.more';
  // 通過元素座標資訊與點選座標資訊,判斷是否被點選
  this.getBoundingClientRect(trackElementName).then((res) => {
    res.boundingClientRect.forEach((item) => {
      const isHit = this.isClickTrackArea(clickInfo, item, res.scrollOffset);
      console.log(isHit, 'isHit')
    });
  });
},
/**
 * 判斷點選是否落在目標元素
 * @param {Object} clickInfo 使用者點選座標
 * @param {Object} boundingClientRect 目標元素資訊
 * @param {Object} scrollOffset 頁面位置資訊
 * @returns {Boolean} 是否被點選
 */
isClickTrackArea(clickInfo, boundingClientRect, scrollOffset) {
  if (!boundingClientRect) return false;
  const { x, y } = clickInfo.detail; // 點選的x y座標
  const { left, right, top, height } = boundingClientRect;
  const { scrollTop } = scrollOffset;
  if (left < x && x < right && scrollTop + top < y && y < scrollTop + top + height) {
    return true;
  }
  return false;
},
/**
 * 獲取頁面元素資訊
 * @param {String} element 元素class或者id
 * @returns {Promise}
 */
getBoundingClientRect (element) {
  return new Promise((reslove) => {
    const query = wx.createSelectorQuery();
    query.selectAll(element).boundingClientRect();
    query.selectViewport().scrollOffset();
    query.exec(res => reslove({ boundingClientRect: res[0], scrollOffset: res[1] }));
  });
}
複製程式碼

2、擴充套件Page方法

由於elementTracker方法需要在Page中定義以供wxml呼叫,如果每個頁面手動編寫就過於繁瑣了,可以通過改寫Page來實現自動擴充套件,程式碼如下

// 記錄原Page方法
const originPage = Page;
// 重寫Page方法
Page = (page) => {
  // 給page物件注入三個方法
  page.elementTracker = function() {}
  page.methodTracker = function() {}
  page.isClickTrackArea = function() {}
  return originPage(page);
};
複製程式碼

3、對頁面函式埋點

有些場景我們除了對頁面元素點選埋點,還要對頁面函式進行埋點,例如使用者下拉重新整理的時候,可以對原方法進行包裝,插入埋點程式碼,方案和第三點差不多。

const originPage = Page;
// 重寫Page方法
Page = (page) => {
  // 給onShow方法插入埋點
  const originMethod = page['onShow'];
  page['onShow'] = function() {
    report() // 記錄埋點
    return originMethod();
  }
  return originPage(page);
};
複製程式碼

4、通過配置表設定埋點

上面介紹了頁面元素和函式的埋點方式,下面講一下如何管理埋點資訊解決程式碼入侵問題,可以把埋點資訊以配置表的方式宣告,以後還可以做到動態配置,在服務端配置完畢下發到客戶端。

const tracks = {
  path: 'pages/film/detail',
  elementTracks: [
    {
      element: '.buy-now',  // 宣告需要監聽的元素
      dataKeys: ['film.filmId'], // 宣告需要獲取Data下的film物件下的filmId欄位
    },
    methodTracks: [
    {
      method: 'toBannerDetail', // 宣告需要監聽的函式
      dataKeys: ['imgUrls'], // 宣告需要獲取Data下的imgUrls資料
    },
  ],
  ]
};
複製程式碼

最後

完整的程式碼已經封裝成SDK了,可以快速整合到專案 github地址

相關文章