詳解微信小程序(Taro)手動埋點和自動埋點的實現(xiàn)
每一個公司要想用戶增長,都要收集和分析用戶操作數(shù)據(jù),因此埋點是必不可少的事情。
而對于前端職業(yè)發(fā)展來說,傳統(tǒng)的手動埋點,無疑是繁瑣又無聊的事情,能簡化就簡化。
一、手動埋點
手動埋點就是在每一處需要的地方,都加一段上報埋點的代碼。影響代碼的閱讀體驗,且散落的埋點代碼不方便管理。
以頁面 pv 為例,我們此前是在每一個頁面中上報 pv:
// src/manual/home/index.tsx import tracking from "./tracking"; // pageSn 是前端和產(chǎn)品約定的「頁面在埋點系統(tǒng)的唯一標識」,比如這個項目首頁的標識符是數(shù)字 11664 const pageSn = 11111; export default () => { // useDidShow 是 Taro 專有的 Hook,等同于小程序原生 componentDidShow 生命周期,會在頁面展示的時候調(diào)用。 useDidShow(() => { // 通過統(tǒng)一封裝的 sendPv 方法發(fā)送 pv 埋點 sendPv(pageSn); }); return <View>手動埋點頁面</View>; };
二、自動埋點
自動埋點可分為全自動埋點和半自動埋點。全自動埋點則是不管需不需要,將所有的點都埋了。前端肯定開心了 “以后埋點產(chǎn)品都不要不要找我啦”,可數(shù)據(jù)同學(xué)就哭唧唧了。
比如,騰訊和 Taro 團隊共同推出 騰訊有數(shù)自動化埋點,接入超級簡單。比如配置 proxyPage 為 true 即可 “上報所有頁面的 browse 、leave、share 等事件”,配置 autoTrack 為 true 即可 “自動上報所有元素的 tap、change、longpress、confirm 事件”。
可從數(shù)據(jù)量和有效性來說,「全埋」等于「不埋」,因為「全埋」一方面對數(shù)據(jù)存儲量要求很高,另一方面會給我們負責(zé)數(shù)據(jù)清洗的同學(xué)帶來大量工作。
所以接下來,還是從中尋求平衡,著重看半自動埋點。
1、頁面曝光(pv)
頁面曝光(pv),理想的上報方式是:
在一個統(tǒng)一的地方(如 trackingConf.ts),配置好每個要埋點的頁面的標識符(即 pageSn)
頁面顯示后,自動判斷下是否需要上報(是否在 trackingConf.ts 配置文件中),要就直接上報。
具體實現(xiàn)
(1)統(tǒng)一配置埋點字段,pageSn 表示頁面在埋點系統(tǒng)中的標識符
// trackingConf.ts export default { "auto/home/index": { pageSn: 11111, }, };
當(dāng)然,如果你的業(yè)務(wù)允許三七二十一,上報所有頁面 pv(帶上 path 讓產(chǎn)品自己篩選),那(1)這步可以省了,直接看(2),這種方式可稱為「pv 全自動埋點」。
(2)封裝 usePv hook,在頁面展示時,獲取當(dāng)前頁面 pageSn、判斷是否要埋 pv、要的話發(fā)送 pv
// usePv.ts // 獲取當(dāng)前頁面 path,借助 Taro 的 getCurrentInstance export const getPath = () => { const path = Taro.getCurrentInstance().router?.path || ""; // 去掉開頭的 /,比如將 '/auto/home/index' 改為 'auto/home/index' return path.match(/^\/*/) ? path.replace(/^\/*/, "") : path; }; // 獲取當(dāng)前頁面 pageSn、判斷是否要埋 pv、要的話發(fā)送 pv // 入?yún)?getExtra 支持攜帶額外參數(shù) 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?.(); // 通過統(tǒng)一封裝的 sendPv 方法發(fā)送 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 即可實現(xiàn)「所有頁面按需埋點」:
// src/auto/home/index.tsx const Index = WrapPage(() => { return <View>自動埋點頁面</View>; });
后續(xù)新開發(fā)一個頁面,除了用 WrapPage 包裹外,只需要在第(1)步的 trackingConf.ts 中增加該頁面的 pageSn 即可。
提問環(huán)節(jié)
好奇寶寶們可能要問了:
(1)WrapPage 里這樣封裝了 usePv(),應(yīng)該如何支持上報自定義字段呢?
舉個例子,產(chǎn)品希望 src/auto/home/index.tsx 這個頁面上報 pv 的時候,額外上報一下 當(dāng)前頁面 URL 查詢參數(shù)即 params。
很簡單,就是這個頁面不要用 WrapPage 包裹,而是拿到 params 后直接調(diào)用 usePv 函數(shù):
// src/auto/home/index.tsx const Index = () => { usePv({ getExtra: () => { const params = Taro.getCurrentInstance().router?.params; return { params }; }, }); return <View>自動埋點頁面</View>; });
(2)這里每個頁面組件,都要用 WrapPage 包裹一下,對業(yè)務(wù)還是有侵入型了,原生小程序可以改寫 Page,在 Page 中直接 usePv()。Taro 項目應(yīng)該也可以這么做,實現(xiàn) 0 業(yè)務(wù)侵入吧?
Taro 項目中,確實可以也可以和原生小程序一樣,在 App 中統(tǒng)一攔截原生 Page,但這樣的話,上面「某些頁面要計算額外參數(shù)并上報」就不好解決了。
2、頁面分享
微信小程序中,存在兩種分享:
- 分享給好友:useShareAppMessage。
- 分享到朋友圈:useShareTimeline。小程序基礎(chǔ)庫 v2.11.3 開始支持,目前只在 Android 平臺可用。
具體實現(xiàn)
以 useShareAppMessage 為例(useShareTimeline 同理):
(1)仍在 trackingConf.ts 統(tǒng)一配置文件中,增加分享埋點的標識字段 eleSn (及額外參數(shù))
// trackingConf.ts export default { "auto/home/index": { pageSn: 11111, shareMessage: { eleSn: 2222, destination: 0 }, // 增加 shareMessage 包含分享好友的 eleSn、業(yè)務(wù)額外參數(shù) destination } };
(2)封裝 useShareAppMessage 方法,業(yè)務(wù)調(diào)用 Taro.useShareAppMessage 的地方全局替換為這個 useShareAppMessage。
// 分享給好友,統(tǒng)一埋點 export const useShareAppMessage = ( callback: (payload: ShareAppMessageObject) => ShareAppMessageReturn ) => { let newCallback = (payload: ShareAppMessageObject) => { const result = callback(payload) const currentPath = getPath(); // getPath 獲取當(dāng)前頁面路徑,可參考「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 方法,發(fā)送分享埋點信息 return result } Taro.useShareAppMessage(newCallback) }
這樣,如果有個頁面需增加分享好友的埋點,直接在 trackingConf.ts 中增加 shareMessage 的 eleSn 即可,useShareTimeline 同理。
提問環(huán)節(jié)
好奇寶寶們可能要問了:頁面需要增加分享好友/朋友圈的埋點,可否 0 配置(即不用修改上述的 trackingConf.ts 文件)?
與前文中「pv 全自動埋點」類似,只要和產(chǎn)品約定好撈數(shù)據(jù)的方式也可以,比如筆者和產(chǎn)品約定了:
每個頁面分享好友/朋友圈,eleSn 都是 444444,然后產(chǎn)品通過 pageSn 判斷是哪個頁面,通過 share_to 判斷是分享好友 / 朋友圈,對于分享好友的場景,再通過 from_ele 判斷通過右上角分享還是點擊頁面中的按鈕分享。
這樣頁面分享也可以全自動埋點了。
3、元素埋點
元素自動埋點的調(diào)研遇到阻力,尚未落地。下文主要談不同思路遇到的問題,有好的建議歡迎評論區(qū)溝通。
我們元素埋點,較高頻的有曝光、點擊事件,中低頻的有滾動、懸停等事件。
手動埋點的方式就是在元素指定事件觸發(fā)的時候,手動執(zhí)行 sendImpr 上報埋點(帶上頁面唯一標識符 pageSn、 元素唯一標識符 eleSn)。
那這個環(huán)節(jié)是否可以省事一些呢?對業(yè)務(wù)無侵入,大概的做法還是:
在 Component 指定事件觸發(fā)增加個 hook -> 判斷是否要上報埋點 -> 滿足條件則上報
問題一分為二:
(1)攔截元素事件回調(diào)
可以攔截并遍歷小程序 Component 接收到的 options.methods,如果是一個自定義函數(shù),則在函數(shù)被調(diào)用的時候判斷第一個參數(shù)(假設(shè)命名為 e)的 type 是否等于 tap 等事件。這時候可以根據(jù) e 等信息決定是否滿足埋點上報條件了。
原生小程序中的實現(xiàn),大致如下:
// App.js App({ onLaunch() { let old = Component Component = function(config) { // 攔截業(yè)務(wù)傳入的 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) // 觸發(fā)點擊事件的時候,按需上報埋點 } methodFn.call(this,...args) } }); // 返回修改后的 conf return conf }
Taro 項目中,不能直接在組件代碼里用 Component,但可以迂回一些的方式實現(xiàn)相同目的,比如:
// 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)如何自動生成元素唯一標識符
目前是通過埋點系統(tǒng)中申請下來的 eleSn 來唯一標識元素的,如果想要自動標識,可細分為:
- XPath:在 pc / mobile 中還可以,但在小程序中不支持直接獲取節(jié)點的 XPath / 根據(jù) XPath 獲取節(jié)點。微信小程序可否支持通過 XPath 獲取 DOM 元素?
- 自動獲取 組件方法名:原生小程序中,因為直接攔截了 Component options 中的 methods,所以在事件觸發(fā)時可以獲取到原始的方法名,但 Taro 項目中不行,因為 methods 被代理了一道,事件觸發(fā)后,你看到的方法名都是 eh。
- AST 解析源碼分析出頁面名、方法名和方法對應(yīng)的注釋來標識元素:Taro 項目中目測只能用這個方法,但成本較大,且「在代碼不斷迭代后,存量數(shù)據(jù)是否還能用」也是個問題,所以筆者未做嘗試。
三、總結(jié)
本文概述了一下微信小程序(Taro)從手動埋點到自動埋點的思路。并按照頁面埋點(pv、分享)以及元素埋點,分析了實現(xiàn)方式:
- 頁面 pv:
- 封裝 usePv,根據(jù)當(dāng)前頁面 path 從配置文件中讀取出 pageSn
- 封裝頁面組件 WrapPage 調(diào)用 usePv()
- 分享好友/朋友圈:自定義 useShareAppMessage、useShareTimeline,根據(jù)當(dāng)前頁面 path 從配置文件中讀取出 pageSn 和分享 eleSn,然后獲取傳入?yún)?shù)后埋點上報
- 元素埋點:提供了改寫 Component 方法來攔截事件回調(diào)的思路,但因元素唯一標識符不能自動獲取,所以不大適合自動化埋點。
到此這篇關(guān)于詳解微信小程序(Taro)手動埋點和自動埋點的實現(xiàn)的文章就介紹到這了,更多相關(guān)小程序手動埋點和自動埋點內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組的表示方法示例
這篇文章主要介紹了JavaScript數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組的表示方法,從數(shù)據(jù)結(jié)構(gòu)線性表的角度分析了數(shù)組的原理并結(jié)合實例形式分析了javascript數(shù)組的定義與使用方法,需要的朋友可以參考下2017-04-04JS獲得選取checkbox整行數(shù)據(jù)的方法
這篇文章主要介紹了JS獲得選取checkbox整行數(shù)據(jù)的方法,涉及使用js對DOM節(jié)點的操作技巧,非常具有實用價值,需要的朋友可以參考下2015-01-01js實現(xiàn)獲取最新本周周一開始的日期(單周日歷卡)
這篇文章主要為大家介紹了js實現(xiàn)獲取最新本周周一開始的日期(單周日歷卡)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10javascript中的offsetWidth、clientWidth、innerWidth及相關(guān)屬性方法
這篇文章主要介紹了javascript中的offsetWidth、clientWidth、innerWidth及相關(guān)屬性方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05