性能監控是性能優化的第一步,相當重要,由於咱們只有進行了性能監控才能知道性能的瓶頸所在,最後對症下藥進行性能的優化。javascript
支持狀況:目前,全部主要瀏覽器都已經支持performance對象,包括Chrome 20+、Firefox 15+、IE 10+、Opera 15+。html
API window.performance.timing
java
從圖中咱們能夠看到該API給了咱們不少數據,咱們能夠使用這些數據對性能進行監控。ajax
// 上報函數
function send(data) {
// 發起ajax請求
}
function handleData(performance) {
let performanceData = {}
if (performance) {
// 重定向時間
performanceData.redirectTime = performance.redirectEnd - performance.redirectStart
// 緩存時間
performanceData.cacheTime = performance.domainLookupStart - performance.fetchStart
// dns查詢時間
performanceData.dnsTime = performance.domainLookupEnd - performance.domainLookupStart
// tcp握手時間
performanceData.TcpTime = performance.connectEnd - performance.connectStart
// ajax請求時間
performanceData.ajaxTime = performance.responseEnd - performance.requestStart
// 開始解析dom時間,此時document.readyState 變爲 loading
performanceData.domLoadingTime = performance.domLoading - performance.navigationStart
// dom解析完成時間,此時document.readyState 變爲 interactive
performanceData.domInteractiveTime = performance.domInteractive - performance.navigationStart
// dom解析完成,資源加載完成,腳本完成
performanceData.domContentLoadedEventEndTime = performance.domContentLoadedEventEnd - performance.navigationStart
// 頁面從開始到結束的所有時間時間
performanceData.loadPageTime = performance.loadEventStart - performance.navigationStart
}
return performanceData
}
// 初始化函數
function init() {
window.onload = function () {
if (window.performance) {
let timing = window.performance.timing;
let performanceData = handleData(timing)
performanceData.timestamp = Date.now()
performanceData.url = location.href
send(performanceData)
}
}
}
let performanceMonitor = {
init
}
export default performanceMonitor
複製代碼
這個函數返回的將是一個數組,包含了頁面中全部的 HTTP 請求api
function getAllSourceTime() {
let allSourceTime = []
if (window.performance && window.performance.getEntries) {
window.performance.getEntries().forEach(function (item) {
let temp = {}
temp.name = item.name
temp.entryType = item.entryType
if (item.entryType === 'paint') {
temp.startTime = item.startTime
} else {
temp.transferSize = item.transferSize
temp.duration = item.duration
temp.initiatorType = item.initiatorType
}
allSourceTime.push(temp)
})
}
return allSourceTime
}
複製代碼
返回的數組中有兩個東西須要咱們注意,一個是first-paint(首次繪製),另外一個是first-contentful-paint(首次內容繪製)。first-paint表示瀏覽器繪製了頁面的第一個像素的那一刻,first-contentful-paint表示第一 bit 的內容被繪製的那一刻。數組
但實際上咱們有的時候並不須要全部資源的請求時間數據,咱們只關心first-paint和first-contentful-paint的時間,所以咱們能夠把上述兩種方法結合一下,讓咱們的監控數據變得有重點。// 獲取first-paint和first-contentful-paint的時間
function getPaintTime() {
let obj = {}
if (window.performance && window.performance.getEntriesByType) {
let paintArr = window.performance.getEntriesByType('paint')
if (paintArr && paintArr.length) {
paintArr.forEach(function (item) {
obj[item.name] = item.startTime
})
}
}
return obj
}
// 初始化函數
function init() {
window.onload = function () {
if (window.performance) {
let timing = window.performance.timing;
let performanceData = handleData(timing)
performanceData.timestamp = Date.now()
performanceData.url = location.href
performanceData = Object.assign({}, performanceData, getPaintTime())
send(performanceData)
}
}
}
複製代碼
function performanceObserver() {
let obj = {}
var observer = new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
if (entry.entryType === 'paint') {
obj[entry.name] = entry.startTime
} else {
let temp = handleData(entry)
obj = Object.assign({}, obj, temp)
}
})
});
observer.observe({ entryTypes: ['paint', 'navigation'] });
return obj
}
performanceObserver()
複製代碼
通過試驗我發現,PerformanceObserver是使用一種觀察者模式去實時的取到那些數據值,而Performance.timing必須放在window.onload函數裏面進行取值。這些差別致使會有一些小問題產生。瀏覽器
問題一:在window.onload函數裏面咱們進行loadEventEnd的取值會取不到,而在PerformanceObserver則不存在這樣的問題;緩存
問題二:使用PerformanceObserver咱們發現沒有navigationStart,domLoading的值。性能優化
問題三:PerformanceObserver更精確。 因此綜合它們,最終咱們的代碼是這樣的:dom
// 上報函數
function send(data) {
// 發起ajax請求
}
// 處理數據
function handleData(performance) {
let navigationStart = performance.navigationStart || performance.fetchStart
let performanceData = {}
if (performance) {
// 重定向時間
performanceData.redirectTime = performance.redirectEnd - performance.redirectStart
// 緩存時間
performanceData.cacheTime = performance.domainLookupStart - performance.fetchStart
// dns查詢時間
performanceData.dnsTime = performance.domainLookupEnd - performance.domainLookupStart
// tcp握手時間
performanceData.TcpTime = performance.connectEnd - performance.connectStart
// ajax請求時間
performanceData.ajaxTime = performance.responseEnd - performance.requestStart
// 開始解析dom時間,此時document.readyState 變爲 loading
performanceData.domLoadingTime = performance.domLoading ? performance.domLoading - navigationStart : null
// dom解析完成時間,此時document.readyState 變爲 interactive
performanceData.domInteractiveTime = performance.domInteractive - navigationStart
// dom解析完成,資源加載完成,腳本完成
performanceData.domContentLoadedEventEndTime = performance.domContentLoadedEventEnd - navigationStart
// 頁面從開始到結束的所有時間時間
performanceData.loadPageTime = performance.loadEventEnd ? performance.loadEventEnd - navigationStart : null
}
return performanceData
}
// 獲取first-paint及first-contentful-paint的時間
function getPaintTime() {
let obj = {}
if (window.performance && window.performance.getEntriesByType) {
let paintArr = window.performance.getEntriesByType('paint')
if (paintArr && paintArr.length) {
paintArr.forEach(function (item) {
obj[item.name] = item.startTime
})
}
}
return obj
}
// performanceObserver進行監控
function performanceObserver() {
let obj = {}
var observer = new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
if (entry.entryType === 'paint') {
obj[entry.name] = entry.startTime
} else {
let temp = handleData(entry)
obj = Object.assign({}, obj, temp)
}
})
obj.from = 'window.PerformanceObserver'
obj.url = location.href
obj.timestamp = Date.now()
send(obj)
});
observer.observe({ entryTypes: ['navigation', 'paint'] });
}
// 初始化函數
function init() {
if (window.PerformanceObserver) {
performanceObserver()
} else if (window.performance) {
window.onload = function () {
let timing = window.performance.timing;
let performanceData = handleData(timing)
performanceData.timestamp = Date.now()
performanceData.url = location.href
performanceData.from = 'window.performance'
performanceData = Object.assign({}, performanceData, getPaintTime())
send(performanceData)
}
}
}
let performanceMonitor = {
init
}
export default performanceMonitor
複製代碼