今天咱們來聊聊前端的監控html
咱們爲何須要前端監控 ?前端
爲了獲取用戶行爲以及跟蹤產品在用戶端的使用狀況,並以監控數據爲基礎,指明產品優化方向ajax
前端監控分爲三類api
- 衡量前端的性能的指標是時間數組
那麼如何監測時間呢, 瀏覽器給咱們提供了一個 API performance
來看看裏面都有什麼吧
promise
它的屬性 timing 是一個PerformanceTiming 對象 , 包含了延遲相關的性能,timing裏的屬性基本都是成雙成對的 都是以xxxstart --- xxxend end跟start的時間差就是咱們所須要的信息 瀏覽器
那麼咱們來捋一捋網頁打開到關閉 都有哪些時間戳記錄下來,看一張網頁加載流程圖
服務器
首先是navigationStart 就是地址欄開始加載 期間會執行unload 卸載上一個網頁的內容 若是有重定向(同域名),就開始重定向。app
fetchStart 抓取開始 頁面開始加載
domainLookupStart dns 解析開始
domainLookupEnd dns 解析結束
connectStart tcp 握手開始
connectEnd 創建鏈接
requestStart 請求頁面開始
responseStart 響應開始
responseEnd 響應結束
domloading dom開始加載
domInteractive dom結構樹加載完成(src外鏈資源未完成)
domcontentLoaded($(function(){})
)
domComplete dom 加載完畢 外鏈資源加載完畢
loadevent (window.onload=function(){}
裏面的代碼加載完畢)dom
那麼咱們開始寫個監控吧 先起一個服務
serve.js
let Koa = require('koa') let Server = require('koa-static') let path = require('path') let app = new Koa() app.use(Server(path.resolve(__dirname))) app.listen(3000,function(){ console.log('Server is running on port 3000') })
將serve做爲靜態資源目錄 服務起在端口3000
再新建performance.js
performance.js
// 性能監控 let time = ()=>{ let timing = performance.timing let data = { prevPage: timing.fetchStart - timing.navigationStart , // 上一個頁面卸載到新頁面的時間 redirect: timing.redirectEnd - timing.redirectStart , // 重定向時長 dns: timing.domainLookupEnd - timing.domainLookupStart, // dns 解析時長 tcp: timing.connectEnd - timing.connectStart ,// tcp 連接時長 respone: timing.responseEnd - timing.requestStart, // 響應時長 ttfb: timing.responseStart - timing.navigationStart, // 首字節接收到 的時長 domReady:timing.domInteractive - timing.domLoading, // dom 準備時長 whiteScreen:timing.domLoading - timing.navigationStart, // 白屏時間 dom:timing.domComplete - timing.domLoading, // dom解析時間 load:timing.loadEventEnd - timing.loadEventStart, total:timing.loadEventEnd - timing.navigationStart } return data } // 由於檢測代碼在load裏執行 因此此時load事件未完成 檢測不到load的時間 因此咱們須要設置一個定時器來監聽load完成 let timeout = (cb)=>{ let timer; let check = ()=>{ // 加載完畢後纔有performance.timing.loadEventEnd if(performance.timing.loadEventEnd){ timer = null cb() }else{ timer = setTimeout(check,100) } } window.addEventListener('load',check,false) } let domReady = (cb)=>{ let timer; let check = ()=>{ // performance.timing.domInteractive if(performance.timing.domInteractive){ timer = null cb() }else{ timer = setTimeout(check,100) } } window.addEventListener('DOMContentLoaded',check,false) } let _performance = { init(cb){ //有可能domloaded 並未加載好 用戶就關閉網頁了 這種狀況也但願監聽到 domReady(()=>{ let data = time() data.type = 'domReady' cb(data) }) // dom徹底加載完畢 timeout(()=>{ let data = time() data.type = 'loaded' cb(data) }) } } // 經過_performance.init(cb) 獲取到監控數據 _performance.init((data)=>{console.log(data)})
html 引入performance.js
運行結果爲:
而後發送圖片到服務器
// 經過_performance.init(cb) 獲取到監控數據 let formatter = (data)=>{ let arr = [] for(key in data){ arr.push(`${key}=${data[key]}`) } return arr.join('&') } _performance.init((data)=>{ // 而後咱們建立一張空圖片把數據發給服務器 let img = new Image() img.src = '/p.gif?' + formatter(data) })
想要監控資源就得獲取到__proto__ 上的getEntriesByType('resource')方法
獲取到的是個數組 包含了資源請求的時間 名字type 之類的 咱們所作的就是拿區咱們本身須要的東西
performance.js
//代碼省略 let resource = performance.getEntriesByType('resource') let data = resource.map(_=>{ return { name:_.name, initatorType:_.initiatorType, duration:_.duration } }) cb(data)
ajax 請求監控就簡單了
performance.js
// 爲了獲取到咱們所須要的參數 咱們改寫一下 XMLHttpRequest.prototype.open(method,url,isAsync) let ajax = { init(cb){ let xhr = window.XMLHttpRequest // 保存原來的open方法 let oldOpen = xhr.prototype.open console.log(oldOpen) xhr.prototype.open = function(method,url,isAsync = true,...args){ this.info = {method,url,isAsync,message:args} return oldOpen.apply(this,arguments) } let oldSend = xhr.prototype.send xhr.prototype.send = function(value){ let start = Date.now() let fn = (type) => () =>{ this.info.time = Date.now() - start this.info.requestSize = value ? value.length : 0 this.info.responeSize = this.responseText.length this.info.type = type cb(this.info) } this.addEventListener('load',fn('success'),false) this.addEventListener('error',fn('error'),false) this.addEventListener('abort',fn('abort'),false) return oldSend.apply(this,arguments) } } } ajax.init((data)=>{ console.log(data) })
index.html
// ajax 請求監控 原生ajax var request = new XMLHttpRequest(); // 新建XMLHttpRequest對象 request.onreadystatechange = function () { // 狀態發生變化時,函數被回調 if (request.readyState === 4) { // 成功完成 // 判斷響應結果: if (request.status === 200) { // 成功,經過responseText拿到響應的文本: } else { // 失敗,根據響應碼判斷失敗緣由: } } else { // HTTP請求還在繼續... } } // 發送請求: request.open('GET', '/api/categories',true,'fet'); request.send();
結果以下
主要是經過window.onerror 事件來捕獲
let errorCatch = { init(cb){ window.addEventListener('error',function(message, source, lineno, colno, error) { this.info = {} let stack = error.stack; let matchUrl = stack.match(/http:\/\/[^\n]*/)[0] // 獲取報錯路徑 this.info.filename = matchUrl.match(/http:\/\/(?:\S*)\.js/) ? matchUrl.match(/http:\/\/(?:\S*)\.js/)[0]:'html' // 獲取報錯文件; let [,row,col] = stack.match(/:(\d+):(\d+)/) // 獲取報錯行 列 this.info = { message:error.message, name:error.name, filename:this.info.filename, //有多是html裏的js報錯 row, col, // 真實上線的時候 須要source-map 去找到真正的行跟列 } cb(this.info) },true) } } errorCatch.init(data=>{console.dir(data)})
須要值得注意的是
promise 沒法用此方法捕獲!! 須要另行監聽另外的事件
另外不一樣域的js文件不能用此方法捕獲詳細 具體查看https://www.jianshu.com/p/315...
主要方式是在document上監聽事件 經過事件委託獲取事件對象 封裝info 傳給後臺
綜上所訴 大體就是這樣 這裏只是簡單的介紹了下 須要深刻了解的小夥伴請自行尋找相關資料, 好比火焰圖之類的