小程序因爲封閉性較強,要像web應用同樣實現靈活的數據收集,會有必定難度。目前開源的埋點SDK,通常採用手動埋點的方式,這種方式有較強的侵入型,同時開發成本較高,爲了解決這個問題就有了該文章。git
以騰訊移動分析的SDK爲例,若是要記錄埋點信息,只要插入一句代碼便可github
// 例如,記錄搜索行爲
search(keyword) {
if (keyword) {
...業務代碼
}
// 埋點代碼
mta.Event.stat("ico_search", {"query":keyword});
}
複製代碼
示例代碼看起來是比較簡潔的,可是埋點須要收集的數據每每不是單一的,複雜的埋點代碼插入業務代碼,會影響代碼的閱讀體驗,並且埋點代碼散落在各個地方,不方便管理。web
因爲手動埋點必須插入到函數中,有時候咱們爲了獲取頁面某一元素點擊信息,產生了一種叫無業務相關埋點,簡單來講就是你的函數定義,就只有埋點代碼,當這種埋點頻繁出現,代碼會被嚴重污染。小程序
// wxml
<view bindtap="track">這只是一個展現view</view>
//js
track() {
mta.Event.stat("eleClick", {"name":xxxxx});
}
複製代碼
另外,因爲PM會頻繁調整埋點信息,而埋點是一個繁瑣又無聊的工做,基於Don't Repeat Yourself 原則,手動埋帶要不得。bash
總結以上,手動埋點有下列問題函數
小程序沒有提供Dom的事件監聽方法,不過咱們能夠經過事件冒泡的方式監聽,在wxml最外層綁定catchtap事件獲取點擊元素的座標,判斷點擊元素與監聽目標的座標是否相交觸發記錄。ui
判斷點擊位置與元素是否相交方法: this
下面爲實現代碼spa
// 小程序監聽頁面點擊,用戶的點擊行爲都會執行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] }));
});
}
複製代碼
因爲elementTracker方法須要在Page中定義以供wxml調用,若是每一個頁面手動編寫就過於繁瑣了,能夠經過改寫Page來實現自動擴展,代碼以下code
// 記錄原Page方法
const originPage = Page;
// 重寫Page方法
Page = (page) => {
// 給page對象注入三個方法
page.elementTracker = function() {}
page.methodTracker = function() {}
page.isClickTrackArea = function() {}
return originPage(page);
};
複製代碼
有些場景咱們除了對頁面元素點擊埋點,還要對頁面函數進行埋點,例如用戶下拉刷新的時候,能夠對原方法進行包裝,插入埋點代碼,方案和第三點差很少。
const originPage = Page;
// 重寫Page方法
Page = (page) => {
// 給onShow方法插入埋點
const originMethod = page['onShow'];
page['onShow'] = function() {
report() // 記錄埋點
return originMethod();
}
return originPage(page);
};
複製代碼
上面介紹了頁面元素和函數的埋點方式,下面講一下如何管理埋點信息解決代碼入侵問題,能夠把埋點信息以配置表的方式聲明,之後還能夠作到動態配置,在服務端配置完畢下發到客戶端。
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地址