您好,登錄后才能下訂單哦!
本篇內容介紹了“如何進行數據采集和數據上報”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
數據采集
性能數據采集
性能數據采集需要使用 window.performance API
https://developer.mozilla.org/zh-CN/docs/Web/API/Performance
Performance 接口可以獲取到當前頁面中與性能相關的信息,它是 High Resolution Time API 的一部分,同時也融合了 Performance Timeline API、Navigation Timing API、 User Timing API 和 Resource Timing API。
從 MDN 的文檔可以看出,window.performance.timing 包含了頁面加載各個階段的起始及結束時間。
這些屬性需要結合下圖一起看,更好理解:
在這里插入圖片描述
為了方便大家理解 timing 各個屬性的意義,我在知乎找到一位網友對于 timing 寫的簡介,在此轉載一下。
timing: { // 同一個瀏覽器上一個頁面卸載(unload)結束時的時間戳。如果沒有上一個頁面,這個值會和fetchStart相同。 navigationStart: 1543806782096, // 上一個頁面unload事件拋出時的時間戳。如果沒有上一個頁面,這個值會返回0。 unloadEventStart: 1543806782523, // 和 unloadEventStart 相對應,unload事件處理完成時的時間戳。如果沒有上一個頁面,這個值會返回0。 unloadEventEnd: 1543806782523, // 第一個HTTP重定向開始時的時間戳。如果沒有重定向,或者重定向中的一個不同源,這個值會返回0。 redirectStart: 0, // 最后一個HTTP重定向完成時(也就是說是HTTP響應的最后一個比特直接被收到的時間)的時間戳。 // 如果沒有重定向,或者重定向中的一個不同源,這個值會返回0. redirectEnd: 0, // 瀏覽器準備好使用HTTP請求來獲取(fetch)文檔的時間戳。這個時間點會在檢查任何應用緩存之前。 fetchStart: 1543806782096, // DNS 域名查詢開始的UNIX時間戳。 //如果使用了持續連接(persistent connection),或者這個信息存儲到了緩存或者本地資源上,這個值將和fetchStart一致。 domainLookupStart: 1543806782096, // DNS 域名查詢完成的時間. //如果使用了本地緩存(即無 DNS 查詢)或持久連接,則與 fetchStart 值相等 domainLookupEnd: 1543806782096, // HTTP(TCP) 域名查詢結束的時間戳。 //如果使用了持續連接(persistent connection),或者這個信息存儲到了緩存或者本地資源上,這個值將和 fetchStart一致。 connectStart: 1543806782099, // HTTP(TCP) 返回瀏覽器與服務器之間的連接建立時的時間戳。 // 如果建立的是持久連接,則返回值等同于fetchStart屬性的值。連接建立指的是所有握手和認證過程全部結束。 connectEnd: 1543806782227, // HTTPS 返回瀏覽器與服務器開始安全鏈接的握手時的時間戳。如果當前網頁不要求安全連接,則返回0。 secureConnectionStart: 1543806782162, // 返回瀏覽器向服務器發出HTTP請求時(或開始讀取本地緩存時)的時間戳。 requestStart: 1543806782241, // 返回瀏覽器從服務器收到(或從本地緩存讀取)第一個字節時的時間戳。 //如果傳輸層在開始請求之后失敗并且連接被重開,該屬性將會被數制成新的請求的相對應的發起時間。 responseStart: 1543806782516, // 返回瀏覽器從服務器收到(或從本地緩存讀取,或從本地資源讀取)最后一個字節時 //(如果在此之前HTTP連接已經關閉,則返回關閉時)的時間戳。 responseEnd: 1543806782537, // 當前網頁DOM結構開始解析時(即Document.readyState屬性變為“loading”、相應的 readystatechange事件觸發時)的時間戳。 domLoading: 1543806782573, // 當前網頁DOM結構結束解析、開始加載內嵌資源時(即Document.readyState屬性變為“interactive”、相應的readystatechange事件觸發時)的時間戳。 domInteractive: 1543806783203, // 當解析器發送DOMContentLoaded 事件,即所有需要被執行的腳本已經被解析時的時間戳。 domContentLoadedEventStart: 1543806783203, // 當所有需要立即執行的腳本已經被執行(不論執行順序)時的時間戳。 domContentLoadedEventEnd: 1543806783216, // 當前文檔解析完成,即Document.readyState 變為 'complete'且相對應的readystatechange 被觸發時的時間戳 domComplete: 1543806783796, // load事件被發送時的時間戳。如果這個事件還未被發送,它的值將會是0。 loadEventStart: 1543806783796, // 當load事件結束,即加載事件完成時的時間戳。如果這個事件還未被發送,或者尚未完成,它的值將會是0. loadEventEnd: 1543806783802 }
通過以上數據,我們可以得到幾個有用的時間
// 重定向耗時 redirect: timing.redirectEnd - timing.redirectStart, // DOM 渲染耗時 dom: timing.domComplete - timing.domLoading, // 頁面加載耗時 load: timing.loadEventEnd - timing.navigationStart, // 頁面卸載耗時 unload: timing.unloadEventEnd - timing.unloadEventStart, // 請求耗時 request: timing.responseEnd - timing.requestStart, // 獲取性能信息時當前時間 time: new Date().getTime(),
還有一個比較重要的時間就是白屏時間,它指從輸入網址,到頁面開始顯示內容的時間。
將以下腳本放在 </head> 前面就能獲取白屏時間。
<script> whiteScreen = new Date() - performance.timing.navigationStart // 通過 domLoading 和 navigationStart 也可以 whiteScreen = performance.timing.domLoading - performance.timing.navigationStart </script>
通過這幾個時間,就可以得知頁面首屏加載性能如何了。
另外,通過 window.performance.getEntriesByType('resource') 這個方法,我們還可以獲取相關資源(js、css、img...)的加載時間,它會返回頁面當前所加載的所有資源。
在這里插入圖片描述
它一般包括以下幾個類型:
sciprt
link
img
css
fetch
other
xmlhttprequest
我們只需用到以下幾個信息:
// 資源的名稱 name: item.name, // 資源加載耗時 duration: item.duration.toFixed(2), // 資源大小 size: item.transferSize, // 資源所用協議 protocol: item.nextHopProtocol,
現在,寫幾行代碼來收集這些數據。
// 收集性能信息 const getPerformance = () => { if (!window.performance) return const timing = window.performance.timing const performance = { // 重定向耗時 redirect: timing.redirectEnd - timing.redirectStart, // 白屏時間 whiteScreen: whiteScreen, // DOM 渲染耗時 dom: timing.domComplete - timing.domLoading, // 頁面加載耗時 load: timing.loadEventEnd - timing.navigationStart, // 頁面卸載耗時 unload: timing.unloadEventEnd - timing.unloadEventStart, // 請求耗時 request: timing.responseEnd - timing.requestStart, // 獲取性能信息時當前時間 time: new Date().getTime(), } return performance } // 獲取資源信息 const getResources = () => { if (!window.performance) return const data = window.performance.getEntriesByType('resource') const resource = { xmlhttprequest: [], css: [], other: [], script: [], img: [], link: [], fetch: [], // 獲取資源信息時當前時間 time: new Date().getTime(), } data.forEach(item => { const arry = resource[item.initiatorType] arry && arry.push({ // 資源的名稱 name: item.name, // 資源加載耗時 duration: item.duration.toFixed(2), // 資源大小 size: item.transferSize, // 資源所用協議 protocol: item.nextHopProtocol, }) }) return resource }
小結
通過對性能及資源信息的解讀,我們可以判斷出頁面加載慢有以下幾個原因:
鴻蒙官方戰略合作共建——HarmonyOS技術社區
資源過多、過大
網速過慢
DOM 元素過多
除了用戶網速過慢,我們沒辦法之外,其他兩個原因都是有辦法解決的,性能優化的文章和書籍網上已經有很多了,有興趣可自行查找資料了解。
PS:其實頁面加載慢還有其他原因,例如沒有使用按需加載、沒有使用 CDN 等等。不過這里我們強調的僅通過對性能和資源信息的解讀來獲取原因。
錯誤數據采集
目前所能捕捉的錯誤有三種:
鴻蒙官方戰略合作共建——HarmonyOS技術社區
資源加載錯誤,通過 addEventListener('error', callback, true) 在捕獲階段捕捉資源加載失敗錯誤。
js 執行錯誤,通過 window.onerror 捕捉 js 錯誤。
promise 錯誤,通過 addEventListener('unhandledrejection', callback)捕捉 promise 錯誤,但是沒有發生錯誤的行數,列數等信息,只能手動拋出相關錯誤信息。
我們可以建一個錯誤數組變量 errors 在錯誤發生時,將錯誤的相關信息添加到數組,然后在某個階段統一上報,具體如何操作請看下面的代碼:
// 捕獲資源加載失敗錯誤 js css img... addEventListener('error', e => { const target = e.target if (target != window) { monitor.errors.push({ type: target.localName, url: target.src || target.href, msg: (target.src || target.href) + ' is load error', // 錯誤發生的時間 time: new Date().getTime(), }) } }, true) // 監聽 js 錯誤 window.onerror = function(msg, url, row, col, error) { monitor.errors.push({ type: 'javascript', row: row, col: col, msg: error && error.stack? error.stack : msg, url: url, // 錯誤發生的時間 time: new Date().getTime(), }) } // 監聽 promise 錯誤 缺點是獲取不到行數數據 addEventListener('unhandledrejection', e => { monitor.errors.push({ type: 'promise', msg: (e.reason && e.reason.msg) || e.reason || '', // 錯誤發生的時間 time: new Date().getTime(), }) })
小結
通過錯誤收集,可以了解到網站發生錯誤的類型及數量,從而做出相應的調整,以減少錯誤發生。完整代碼和 DEMO 會在文章末尾放出,大家可以復制代碼(HTML文件)在本地測試一下。
數據上報
性能數據上報
性能數據可以在頁面加載完之后上報,盡量不要對頁面性能造成影響。
window.onload = () => { // 在瀏覽器空閑時間獲取性能及資源信息 // https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback if (window.requestIdleCallback) { window.requestIdleCallback(() => { monitor.performance = getPerformance() monitor.resources = getResources() }) } else { setTimeout(() => { monitor.performance = getPerformance() monitor.resources = getResources() }, 0) } }
當然,你也可以設一個定時器,循環上報。不過每次上報最好做一下對比去重再上報,避免同樣的數據重復上報。
錯誤數據上報
我在 DEMO 里提供的代碼,是用一個 errors 數組收集所有的錯誤,再在某一階段統一上報(延時上報)。
其實,也可以改成在錯誤發生時上報(即時上報)。這樣可以避免“收集完錯誤,但延時上報還沒觸發,用戶卻已經關掉網頁導致錯誤數據丟失”的問題。
// 監聽 js 錯誤 window.onerror = function(msg, url, row, col, error) { const data = { type: 'javascript', row: row, col: col, msg: error && error.stack? error.stack : msg, url: url, // 錯誤發生的時間 time: new Date().getTime(), } // 即時上報 axios.post({ url: 'xxx', data, }) }
經網友提醒,可以使用 navigator.sendBeacon() 來進行上報。
window.addEventListener('unload', logData, false); function logData() { navigator.sendBeacon("/log", analyticsData); }
它的技術特點是:
使用 sendBeacon() 方法會使用戶代理(瀏覽器)在有機會時異步地向服務器發送數據,同時不會延遲頁面的卸載或影響下一導航的載入性能。這就解決了提交分析數據時的所有的問題:數據可靠,傳輸異步并且不會影響下一頁面的加載。
擴展
SPA
window.performance API 是有缺點的,在 SPA 切換路由時,window.performance.timing 的數據不會更新。所以我們需要另想辦法來統計切換路由到加載完成的時間。拿 Vue 舉例,一個可行的辦法就是切換路由時,在路由的全局前置守衛 beforeEach 里獲取開始時間,在組件的 mounted 鉤子里執行 vm.$nextTick 函數來獲取組件的渲染完畢時間。
router.beforeEach((to, from, next) => { store.commit('setPageLoadedStartTime', new Date()) })
mounted() { this.$nextTick(() => { this.$store.commit('setPageLoadedTime', new Date() - this.$store.state.pageLoadedStartTime) }) }
除了性能和錯誤監控,其實我們還可以收集更多的信息。
用戶信息收集
navigator
使用 window.navigator 可以收集到用戶的設備信息,操作系統,瀏覽器信息...
UV(Unique visitor)
是指通過互聯網瀏覽這個網頁的訪客,00:00-24:00 內相同的設備訪問只被計算一次。一天內同個訪客多次訪問僅計算一個 UV。
在用戶訪問網站時,可以生成一個隨機字符串+時間日期,保存在本地。在網頁發生請求時(如果超過當天24小時,則重新生成),把這些參數傳到后端,后端利用這些信息生成 UV 統計報告。
PV(Page View)
即頁面瀏覽量或點擊量,用戶每 1 次對網站中的每個網頁訪問均被記錄 1 個PV。用戶對同一頁面的多次訪問,訪問量累計,用以衡量網站用戶訪問的網頁數量。
頁面停留時間
傳統網站
用戶在進入 A 頁面時,通過后臺請求把用戶進入頁面的時間捎上。過了 10 分鐘,用戶進入 B 頁面,這時后臺可以通過接口捎帶的參數可以判斷出用戶在 A 頁面停留了 10 分鐘。
SPA
可以利用 router 來獲取用戶停留時間,拿 Vue 舉例,通過 router.beforeEach、destroyed 這兩個鉤子函數來獲取用戶停留該路由組件的時間。
瀏覽深度
通過 document.documentElement.scrollTop 屬性以及屏幕高度,可以判斷用戶是否瀏覽完網站內容。
頁面跳轉來源
通過 document.referrer 屬性,可以知道用戶是從哪個網站跳轉而來。
小結
通過分析用戶數據,我們可以了解到用戶的瀏覽習慣、愛好等等信息,想想真是恐怖,毫無隱私可言。
DEMO
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script> function monitorInit() { const monitor = { // 數據上傳地址 url: '', // 性能信息 performance: {}, // 資源信息 resources: {}, // 錯誤信息 errors: [], // 用戶信息 user: { // 屏幕寬度 screen: screen.width, // 屏幕高度 height: screen.height, // 瀏覽器平臺 platform: navigator.platform, // 瀏覽器的用戶代理信息 userAgent: navigator.userAgent, // 瀏覽器用戶界面的語言 language: navigator.language, }, // 手動添加錯誤 addError(error) { const obj = {} const { type, msg, url, row, col } = error if (type) obj.type = type if (msg) obj.msg = msg if (url) obj.url = url if (row) obj.row = row if (col) obj.col = col obj.time = new Date().getTime() monitor.errors.push(obj) }, // 重置 monitor 對象 reset() { window.performance && window.performance.clearResourceTimings() monitor.performance = getPerformance() monitor.resources = getResources() monitor.errors = [] }, // 清空 error 信息 clearError() { monitor.errors = [] }, // 上傳監控數據 upload() { // 自定義上傳 // axios.post({ // url: monitor.url, // data: { // performance, // resources, // errors, // user, // } // }) }, // 設置數據上傳地址 setURL(url) { monitor.url = url }, } // 獲取性能信息 const getPerformance = () => { if (!window.performance) return const timing = window.performance.timing const performance = { // 重定向耗時 redirect: timing.redirectEnd - timing.redirectStart, // 白屏時間 whiteScreen: whiteScreen, // DOM 渲染耗時 dom: timing.domComplete - timing.domLoading, // 頁面加載耗時 load: timing.loadEventEnd - timing.navigationStart, // 頁面卸載耗時 unload: timing.unloadEventEnd - timing.unloadEventStart, // 請求耗時 request: timing.responseEnd - timing.requestStart, // 獲取性能信息時當前時間 time: new Date().getTime(), } return performance } // 獲取資源信息 const getResources = () => { if (!window.performance) return const data = window.performance.getEntriesByType('resource') const resource = { xmlhttprequest: [], css: [], other: [], script: [], img: [], link: [], fetch: [], // 獲取資源信息時當前時間 time: new Date().getTime(), } data.forEach(item => { const arry = resource[item.initiatorType] arry && arry.push({ // 資源的名稱 name: item.name, // 資源加載耗時 duration: item.duration.toFixed(2), // 資源大小 size: item.transferSize, // 資源所用協議 protocol: item.nextHopProtocol, }) }) return resource } window.onload = () => { // 在瀏覽器空閑時間獲取性能及資源信息 https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback if (window.requestIdleCallback) { window.requestIdleCallback(() => { monitor.performance = getPerformance() monitor.resources = getResources() console.log('頁面性能信息') console.log(monitor.performance) console.log('頁面資源信息') console.log(monitor.resources) }) } else { setTimeout(() => { monitor.performance = getPerformance() monitor.resources = getResources() console.log('頁面性能信息') console.log(monitor.performance) console.log('頁面資源信息') console.log(monitor.resources) }, 0) } } // 捕獲資源加載失敗錯誤 js css img... addEventListener('error', e => { const target = e.target if (target != window) { monitor.errors.push({ type: target.localName, url: target.src || target.href, msg: (target.src || target.href) + ' is load error', // 錯誤發生的時間 time: new Date().getTime(), }) console.log('所有的錯誤信息') console.log(monitor.errors) } }, true) // 監聽 js 錯誤 window.onerror = function(msg, url, row, col, error) { monitor.errors.push({ type: 'javascript', // 錯誤類型 row: row, // 發生錯誤時的代碼行數 col: col, // 發生錯誤時的代碼列數 msg: error && error.stack? error.stack : msg, // 錯誤信息 url: url, // 錯誤文件 time: new Date().getTime(), // 錯誤發生的時間 }) console.log('所有的錯誤信息') console.log(monitor.errors) } // 監聽 promise 錯誤 缺點是獲取不到行數數據 addEventListener('unhandledrejection', e => { monitor.errors.push({ type: 'promise', msg: (e.reason && e.reason.msg) || e.reason || '', // 錯誤發生的時間 time: new Date().getTime(), }) console.log('所有的錯誤信息') console.log(monitor.errors) }) return monitor } const monitor = monitorInit() </script> <link rel="stylesheet" href="test.css"> <title>Document</title> </head> <body> <button class="btn1">錯誤測試按鈕1</button> <button class="btn2">錯誤測試按鈕2</button> <button class="btn3">錯誤測試按鈕3</button> <img src="https://avatars3.githubusercontent.com/u/22117876?s=460&v=4" alt=""> <img src="test.png" alt=""> <script src="192.168.10.15/test.js"></script> <script> document.querySelector('.btn1').onclick = () => { setTimeout(() => { console.log(button) }, 0) } document.querySelector('.btn2').onclick = () => { new Promise((resolve, reject) => { reject({ msg: 'test.js promise is error' }) }) } document.querySelector('.btn3').onclick = () => { throw ('這是一個手動扔出的錯誤') } </script> </body> </html>
“如何進行數據采集和數據上報”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。