原文鏈接:https://www.cnblogs.com/yalong/p/15954026.html
背景
公司有大量單頁應用的項目,有的年代久遠,想把一些不用的項目下掉,或者把使用頻率很低的工具進行功能重組,避免占用大量人力去維護眾多項目;
但是不知道這些系統是否有人,以及有多少人在用,以及哪些頁面使用頻率最高;
所以需要統計項目的pv,uv
需求分析
- 由於單頁應用的路由跟后端沒有交互,所以后端無法統計到具體的pv,最多統計個uv,所以前端實現比較合適
- 由於系統很多,而且vue, react都有,關鍵版本也不一致,所以設計方案要兼容多框架,多版本
- 還是由於系統太多,所以要盡量減少對現有代碼的侵入,降低接入成本
- 既然要實現統計pv,uv,那么能不能統計多一點的數據,比如首屏加載耗時,異常信息等
- 那么就搞一套適合當下的前端監控吧
監控系統數據采集功能拆解
前端監控的核心是采集相關數據,然后才上報,這里只說數據的采集
一.采集首屏加載相關數據
首屏加載性能指標如下:
- DNS 解析耗時: domainLookupEnd - domainLookupStart
- TCP 連接耗時: connectEnd - connectStart
- SSL 安全連接耗時: connectEnd - secureConnectionStart
- 網絡請求耗時 (TTFB): responseStart - requestStart
- 數據傳輸耗時: responseEnd - responseStart
- DOM 解析耗時: domInteractive - responseEnd
- 資源加載耗時: loadEventStart - domContentLoadedEventEnd
- First Byte時間: responseStart - domainLookupStart
- 白屏時間: responseEnd - fetchStart
- 首次可交互時間: domInteractive - fetchStart
- DOM Ready 時間: domContentLoadEventEnd - fetchStart
- 頁面完全加載時間: loadEventStart - fetchStart
- http 頭部大小: transferSize - encodedBodySize
- 重定向次數:performance.navigation.redirectCount
- 重定向耗時: redirectEnd - redirectStart
這么多指標我們選擇三個指標,代碼如下:
let fn = () => {
let timing = performance.getEntriesByType('navigation')[0]
let TTI = timing.domInteractive - timing.fetchStart; // 首次可交互時間
let L = timing.loadEventStart - timing.fetchStart; // 頁面完全加載時間
let FP = timing.responseEnd - timing.fetchStart; // 白屏時間
let obj = {
TTI, L, FP
}
console.log(obj)
}
window.addEventListener('load', fn)
其他指標獲取方式相同
二.采集pv uv
問題分析
采集PV UV的核心是監聽瀏覽器路由變化,這就不得不提到vue-router 跟react-router了
在每個項目中是可以專門寫各自的router監聽,但是成本太高,代碼侵入太強,而且不同框架,不同版本寫法還不一樣
所以就不准備使用框架本身的router,用原生js寫一套統一路由監聽多好,這樣就不得不先了解下vue-router 跟react-router的實現原理了
vue-router react-router實現原理
這倆的實現原理網上有很詳細的說明,有需要可自行百度,這里就簡單說下:
以vue-router為例,router有兩種模式: hash模式 和 history模式
hash模式就是改變hash,通過 hashchange 可以監聽hash改變
history模式就是正常的url地址,通過history.replaceState history.pushState 實現
MDN上說 replaceState 和 pushState 會觸發 popState,但是親測,觸發不了,要想監聽這倆方法,只能監聽方法本身,也就是 window.addEventListener('replaceState', historyFn);
同時不管哪種模式下都可以通過 history.go/back/forward 以及瀏覽器的前進后退來改變瀏覽器的url
瀏覽器前進后退操作、history.go/back/forward調用、hashchange的時候觸發popState
總結
要想監聽瀏覽器的url變化,只需監聽如下
window.addEventListener('hashchange', hashFn);
window.addEventListener('replaceState', historyFn);
window.addEventListener('pushState', historyFn);
window.addEventListener('popstate', historyFn);
這樣就囊括了 hash變化,replaceState,pushState, 瀏覽器前進后退,history.go/back/forward;
但是hash改變的時候,會同時觸發 hashChange 和 popState,
可以通過一個公共變量,hashFn、historyFn 每次執行都給這個變量temp賦值,在觸發數據上報的時候,判斷下,如果temp變量的值 跟現在的url地址是一樣的,就代表已經上報了該url的數據了,阻止當前上報行為即可
代碼如下:
let tempUrl = ''
let historyFn = (e) => {
let href = e.currentTarget.location.href
if (tempUrl === href) { // 已經觸發過了
return false
} else { // 沒有觸發過,就賦值
tempUrl = href
}
console.log('history change')
}
let hashFn = (e) => {
let href = e.newURL
if (tempUrl === href) {
return false
} else {
tempUrl = href
}
console.log('hash change')
}
window.addEventListener('hashchange', hashFn);
window.addEventListener('replaceState', historyFn);
window.addEventListener('pushState', historyFn);
window.addEventListener('popstate', historyFn);
三.采集異常報錯
異常數據捕獲其實還是挺復雜的,因為上線以后,代碼都是編譯壓縮后的代碼,而且vue、react 框架本身也會對異常進行捕獲並進行console.error
調研過sentry,這個工具挺好的,但是接入成本比較高,結合當前狀態,那么就退而求其次,監聽console.error不就行了嗎,不要求看解壓后的詳細代碼,就看個異常輸出就行,對於啥內存泄漏,頁面崩潰,這些就不需要了。
具體代碼如下:
const monitorErrorInitFn = () => {
/**
* console.error 打印的錯誤,就是要處理上報的信息
*/
const oldErrorLog = console.error;
console.error = null // 這一步是避免重復添加
console.error = function(str) {
oldErrorLog(str)
console.log('看我,我監聽了 error 輸出')
}
}
monitorErrorInitFn()
這樣做是比較草率的,如果需要詳細的報錯信息可以考慮接入sentry 或者其他成熟的工具
項目整體設計
目前為止,數據搜集已經做到了,那么如何讓開發者方便,快捷的接入使用呢?
步驟如下:
1.首先監控系統有個自己的管理后台,用戶在監控后台填寫要接入的項目名稱,項目線上域名,用戶信息存放的位置,然后系統把這些數據保存在數據庫中並生成唯一id
2.把的數據搜集工具封裝成一個npm包,名字就叫u-spa
吧,這個npm包對外暴露一個方法,並把id作為參數穿傳進來
3.u-spa
,初始化的時候,通過id向后端發送請求,獲取用戶填寫的信息,包括系統名稱,線上域名,用戶信息存放位置,並把這些數據存放在 localStorage 中
4.觸發數據上報的時候,就可以把需要采集的數據,並結合localStorage 中的網站信息,獲取當前用戶名,並限制在當前域名下,一起上報到后端,over
ps:
1.填寫線上域名的原因是只處理線上的數據采集,本地、測試環境就不用采集和上報了
2.填寫用戶信息存放的位置(可以在 localStorage中,也可以在 cookie中),是為了明確知道當前用戶是誰,如果接入的系統本身沒有存儲用戶信息,那么在npm初始化請求的時候,后端可以生成一個唯一的 userId,並存放在redis中,設限24小時過期時間,以此 userId 作為用戶信息的標識,不過這種情況下,只能統計當天的pu、uv,比如用戶第一天使用了,第二天沒使用,那么第三天生成的 userId 就跟之前的不一樣了,這樣統計n天之內的數據就不太准確了,所以還是建議上報真實的用戶名或者郵箱
u-spa
u-spa 就是咱們封裝的收集數據並上報的工具
代碼地址: https://github.com/YalongYan/u-spa
安裝如下
npm i u-spa -S
使用如下
import uSpa from 'u-spa'
uSpa(id)
就是在入口文件里添加上面兩行代碼就可以了,id就是在監控后台填寫完網站信息,生成的id
現在只是在觸發上報的是,console出了上報信息,真實使用還需要搭配上傳數據接口。
特性
1.采集頁面首次加載的性能指標,並上報
2.監聽url改變,並上報數據,包括首頁也會觸發上報
3.監聽console.error 並上報打印的數據
4.接入方便,只需兩行代碼
5.無技術壁壘,不管是vue 還是 react都可以支持
6.其實u-spa 不只是限制在單頁應用上使用,多頁應用也是可以用的
總結
其實前端監控,需要監控,以及可以監控的數據,還有很多
比如可以統計頁面的訪問時長、記錄頁面點擊區域並生成點擊熱力圖、統計按鈕級別的點擊事件等等
需要從實際需求出發, 滿足業務需求才是第一位
本文只說數據的采集,其實搭配還需要有后端服務,對數據的接受,以及把這些數據通過圖標等形式展示出來,這樣才是一個完整的監控系統