前言
小程式由於封閉性較強,要像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、通過事件冒泡監聽元素是否被點選
小程式沒有提供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地址