摘要:同時,還要攔截微信發(fā)生網(wǎng)絡(luò)請求的方法,這樣可以拿到網(wǎng)絡(luò)請求相關(guān)的數(shù)據(jù),最后,為了能統(tǒng)計到錯誤,還需要攔截微信發(fā)生錯誤的方法。
某些情況下我們需要對小程序某些用戶的行為進(jìn)行數(shù)據(jù)進(jìn)行統(tǒng)計,比如統(tǒng)計某個頁面的UV,
PV等,統(tǒng)計某個功能的使用情況等。好讓產(chǎn)品對于產(chǎn)品的整個功能有所了解。
在網(wǎng)頁里,我們很多人都用過谷歌統(tǒng)計,小程序里也有一些第三方數(shù)據(jù)統(tǒng)計的庫, 比如騰訊的MTA等等。
但是,第三方的數(shù)據(jù)統(tǒng)計庫要么功能太簡單,滿足不了需求,要么就是要收費(fèi)。(留下了貧窮的淚水。)
等等,又不是你出錢,怕啥? 貴一點就貴一點呀。
嗯,說的沒錯。但是,公司團(tuán)隊內(nèi)部想實現(xiàn)一套完整的自己的數(shù)據(jù)統(tǒng)計系統(tǒng)以滿足自己的需求。所以,還是沒有用第三方的。
所以,具體要統(tǒng)計些啥?
產(chǎn)品經(jīng)理想知道用戶都是怎么進(jìn)入我們的小程序的?
用戶在我們小程序里那個頁面停留的時間最長?平均用戶停留時間是多少?
想知道我們最近開發(fā)的那個功能用的人多不多?
想統(tǒng)計小程序里的一些按鈕有多少用戶點擊了
開發(fā)自己總是很難復(fù)現(xiàn)用戶端出現(xiàn)的bug,
要是可以知道用戶端發(fā)生錯誤時,知道用戶當(dāng)時的用的手機(jī)型號,微信版本,網(wǎng)絡(luò)環(huán)境,頁面參數(shù),和錯誤信息就好了
想知道我們小程序啟動時間是多少?
接口在用戶端的平均響應(yīng)時間是多少ms? 哪些接口報錯了
針對產(chǎn)品經(jīng)理的需求,我們可以知道,Ta想要的是就是數(shù)據(jù)統(tǒng)計要實現(xiàn)的功能。對于開發(fā)來說,我們關(guān)注的更多就是錯誤統(tǒng)小程序性能這塊的東西。
好,到這里,我們需求是明白了。就是要實現(xiàn)一套既能統(tǒng)計普通的埋點數(shù)據(jù),也要能統(tǒng)計到小程序里一些特殊觸發(fā)的事件,比如appLaunch, appHide 等,還要可以統(tǒng)計錯誤。
好,那先來看看如何實現(xiàn)產(chǎn)品的需求吧
用戶進(jìn)入小程序可以在 小程序 onLaunch 回調(diào)里拿到參數(shù) 的scene 值,這樣就可以知道用戶是怎么進(jìn)入小程序的了。小case, 難不到我。
嗯,第一個需求實現(xiàn)了,那如何統(tǒng)計第二個呢?如何統(tǒng)計某個頁面的停留時間呢?
這也難不倒我,用戶在進(jìn)入頁面時會觸發(fā)onShow 事件, 同樣,在離開頁面(或者切后臺時)會觸發(fā)onHide事件,我只需要在onShow里記錄一下時間,同時在onHide 里也記錄一下時間,把兩個時間一減就可以了。
Page({ data: { beginTime: 0, endTime: 0 }, onShow: function() { // Do something when page show. this.setData({ beginTime: new Date().getTime() }) }, onHide: function() { // Do something when page hide. let stayTime = new Date().getTime() - this.beginTime; // 這個就是用戶在這個頁面的停留時間了 }, })
等等,這樣確實實現(xiàn)了需求,萬一產(chǎn)品要統(tǒng)計所有也面的停留時長? 那我們豈不要在每一個頁面都這樣寫一遍?有沒有更好的方法呢?
好,接下來就是數(shù)據(jù)統(tǒng)計實現(xiàn)的要點了,即攔截微信原生事件,這樣可以在某個特殊事件觸發(fā)時,做一些我們統(tǒng)計的事情。同時,還要攔截微信發(fā)生網(wǎng)絡(luò)請求的方法,這樣可以拿到網(wǎng)絡(luò)請求相關(guān)的數(shù)據(jù),最后,為了能統(tǒng)計到錯誤,還需要攔截微信發(fā)生錯誤的方法。
1.特殊事件的監(jiān)聽 App(Object object)注冊小程序。接受一個 Object 參數(shù),其指定小程序的生命周期回調(diào)等。
App() 必須在 app.js 中調(diào)用,必須調(diào)用且只能調(diào)用一次。不然會出現(xiàn)無法預(yù)期的后果。
攔截全局的事件:
下面是小程序官方文檔對于App 注冊方法的文檔:
App({ onLaunch (options) { // Do something initial when launch. }, onShow (options) { // Do something when show. }, onHide () { // Do something when hide. }, onError (msg) { console.log(msg) }, globalData: "I am global data" })
假如我們要在小程序onLaunch 時打印一句hello Word,我們有哪些方法實現(xiàn)?
方法1:
直接寫在onLaunch方法里
onLaunch (options) { console.log("hello World") }
方法2:
使用 monkey patch方法 猴子補(bǔ)丁(monkey patch)
猴子補(bǔ)丁主要有以下幾個用處:
在運(yùn)行時替換方法、屬性等
在不修改第三方代碼的情況下增加原來不支持的功能
在運(yùn)行時為內(nèi)存中的對象增加patch而不是在磁盤的源代碼中增加
舉個栗子,假如我們在console.log 方法里都先打印出當(dāng)前的時間戳,我們可以這樣:
var oldLog = console.log console.log = function() { oldLog.call(this, new Date().getTime()) oldLog.apply(this, arguments) }
同理,我們針對onLaunch 進(jìn)行猴子補(bǔ)丁
var oldAp = App App = function(options) { var oldOnLaunch = options.onLaunch options["onLaunch"] = function(t) { // 做一些我們自己想做的事情 console.log("hello word....") // 調(diào)用原來的onLaunch 方法 oldOnLaunch.call(this, t) } // 調(diào)用原來的App 方法 oldApp(options) // 想像一下,小程序內(nèi)部調(diào)用onLaunch 方法應(yīng)該是這樣子的: options.onLaunch(params) } // 問題,有的時候,我們可能沒有注冊某一個事件,比如頁面的onShow, 所有,我們在替換的時候還需要判斷一下參數(shù)是否傳了對應(yīng)的方法 Page({ onLoad (options) {}, onHide (options) {} }) // 針對這種情況,我們需要這樣寫 var oldPage = Page Page = function(options) { if (options["onShow"]) { // 如過有注冊onShow 這個回調(diào) var oldOnShow = options.onShow // onShow 方法調(diào)用時都是 傳了一個對象 options["onShow"] = function(t) { // doSomething() oldOnShow.call(this, t) } } // 調(diào)用原來的Page 方法。 oldPage.apply(null, [].slice.call(arguments)) // 注意: 下面這兩種寫都會報錯: VM23356:1 Options is not object: {"0":{}} in pages/Badge.js 問題具體原因暫時未找到。 // oldPage.call(null, arguments) // oldPage(arguments) }
通過上面的方法,我們可以攔截了 App 方法注冊的一些全局方法,比如 onLaunch , onShow, onHide, 和Page 注冊的事件如 onShow, onHide, onLoad, onPullDownRefresh, 等頁面注冊事件。
2.網(wǎng)絡(luò)請求的監(jiān)聽思路: 攔截微信的請求事件。
let Request = { request: function (e) { let success = e[0].success, fail = e[0].fail, beginTime = smaUtils.getTime(), endTime = 0 // 攔截請求成功方法 e[0].success = function () { endTime = smaUtils.getTime() const performance = { type: constMap.performance, event: eventMap.wxRequest, url: e[0].url, status: arguments[0].statusCode, begin: beginTime, end: endTime, total: endTime - beginTime } smaUtils.logInfo("success performance:", performance) // 這里做上報的事情 // SMA.performanceReport(performance) success && success.apply(this, [].slice.call(arguments)) } // 攔截請求失敗方法 e[0].fail = function () { endTime = smaUtils.getTime() const performance = { type: constMap.performance, event: eventMap.wxRequest, url: e[0].url, status: arguments[0].statusCode, begin: beginTime, end: endTime, total: endTime - beginTime } smaUtils.logInfo("fail performance:", performance) // 這里做上報的事情 // SMA.performanceReport(performance) fail && fail.apply(this, [].slice.call(arguments)) } }, } // 替換微信相關(guān)屬性 let oldWx = wx, newWx = {} for (var p in wx) { if (Request[p]) { let p2 = p.toString() newWx[p2] = function () { Request[p2](arguments) // 調(diào)用原來的wx.request 方法 oldWx[p2].apply(oldWx, [].slice.call(arguments)) } } else { newWx[p] = oldWx[p] } } // eslint-disable-next-line wx = newWx
疑惑:為什么要使用替換整個wx對象的方法呢? 不直接用我們的request 方法 替換 wx.request 方法
var oldRequest = wx.request wx.request = function(e) { // doSomething(); console.log("請求攔截操作...") oldRequest.call(this, e); // 調(diào)用老的request方法 } // 結(jié)果報錯了: // TypeError: Cannot set property request of [object Object] which has only a getter3.錯誤的監(jiān)聽
3.1 攔截App里注冊的 onError事件
var oldAp = App App = function(options) { var oldOnError = options.onErrr options["onErrr"] = function(t) { // 做一些我們自己想做的事情 console.log("統(tǒng)計錯誤....", t) // 調(diào)用原來的onLaunch 方法 oldOnError.call(this, t) } // 調(diào)用原來的App 方法 oldApp(options) }
3.2 攔截 conole.error
console.error = function() { var e = [].slice.call(arguments) if (!e.length) { return true } const currRoute = smaUtils.getPagePath() // 統(tǒng)計錯誤事件 // SMA.errorReport({event: eventMap.onError, route: currRoute, errrMsg: arguments[0]}) smaUtils.logInfo("捕捉到error 事件,", e) oldError.apply(console, e) }
至此,我們已經(jīng)有能力在小程序發(fā)起請求時,發(fā)生錯誤時,生命周期或者特殊函數(shù)回調(diào)時,我們都能在里面做一些我們想要的數(shù)據(jù)統(tǒng)計功能了。
說了這么多大家估計也看累了。鑒于篇幅,具體的代碼就不在這里貼了。
最終實現(xiàn)的數(shù)據(jù)統(tǒng)計模塊大致實現(xiàn)了以下功能:
普通埋點信息上報功能
錯誤信息上報功能
性能數(shù)據(jù)上報功能
具體的上報時機(jī)支持配置
支持指定網(wǎng)絡(luò)環(huán)境上報
支持統(tǒng)計數(shù)據(jù)緩存到微信本地功能
整個統(tǒng)計代碼的配置文件如下:
const wxaConfig = { project: "myMiniProgram", // 項目名稱 trackUrl: "https://youhost.com/batch", // 后臺數(shù)據(jù)統(tǒng)計接口 errorUrl: "https://youhost.com/batch", // 后臺錯誤上報接口 performanceUrl: "https://youhost.com/batch", // 后臺性能上報接口 version: "0.1", prefix: "_wxa_", priority: ["track", "performance", "error"], // 發(fā)送請求的優(yōu)先級,發(fā)送時,會依次發(fā)送 useStorage: true, // 是否開啟storage緩存 debug: false, // 是否開啟調(diào)試(顯示log) autoTrack: true, // 自動上報 onShow, onHide, 分享等 內(nèi)置事件 errorReport: false, // 是否開啟錯誤上報 performanceReport: false, // 接口性能上報 maxReportNum: 20, // 當(dāng)次上報最大條數(shù) intervalTime: 15, // 定時上報的時間間隔,單位 s, 僅當(dāng)開啟了定時上報有效。 networkList: ["wifi", "4g", "3g"], // 允許上報的網(wǎng)絡(luò)環(huán)境 opportunity: "pageHide" // pageHide、appHide、realTime(實時上報)、timing(定時上報) 上報的時機(jī),四選一 } export default wxaConfig
具體上報時,上報的數(shù)據(jù)結(jié)構(gòu)大致長這樣:
項目已傳到GitHub -> GitHub傳送門-wxa
如果這篇文章幫到你了,覺得不錯的話來點個Star吧
你們是如何實現(xiàn)小程序數(shù)據(jù)統(tǒng)計的呢? 歡迎在評論里留言交流~~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/105015.html
摘要:微信小程序運(yùn)維中心提供了錯誤日志記錄,但功能還是比較有限。有時候一個微信小程序可能會用到多個第三方服務(wù),從多個域名獲取數(shù)據(jù)。要使用監(jiān)控,你需要去網(wǎng)站注冊賬號并創(chuàng)建一個微信小程序監(jiān)控項目,然后按照提示接入插件。 在微信小程序里,與后臺服務(wù)器交互的主要接口函數(shù)是wx.request(),用于發(fā)起 HTTPS 網(wǎng)絡(luò)請求。其重要性不言而喻。然而,卻經(jīng)常遇到請求失敗的問題,筆者特意谷歌wx.re...
摘要:更新修正協(xié)議下默認(rèn)關(guān)閉時會取消訂閱本次訂閱的所有物聯(lián)網(wǎng)整體方案篇年,云巴利用實時跨平臺雙向物聯(lián)網(wǎng)云端系統(tǒng)的產(chǎn)品優(yōu)勢,積極迎接物聯(lián)網(wǎng)新時代,為客戶定制物聯(lián)網(wǎng)整體解決方案。 SDK 篇 Android SDK 更新 ·Android Release 1.8.5在demo中去除掉小米、華為平臺的jar包,讓用戶自行下載 ·Android SDK 1.8.6對新版華為推送做了適配。 JavaS...
閱讀 2837·2021-09-29 09:34
閱讀 3501·2021-09-23 11:21
閱讀 2685·2021-09-06 15:00
閱讀 1265·2019-08-30 15:44
閱讀 2170·2019-08-29 17:23
閱讀 3174·2019-08-29 16:44
閱讀 3198·2019-08-29 13:13
閱讀 2124·2019-08-28 18:12