性能監控

性能監控是性能優化的第一步,相當重要,由於咱們只有進行了性能監控才能知道性能的瓶頸所在,最後對症下藥進行性能的優化。javascript

關於Performance.timing

  • 支持狀況:目前,全部主要瀏覽器都已經支持performance對象,包括Chrome 20+、Firefox 15+、IE 10+、Opera 15+。html

  • API window.performance.timingjava

    window.performance.timing

從圖中咱們能夠看到該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
複製代碼

關於Performance.getEntries() 獲取全部資源請求的時間數據

這個函數返回的將是一個數組,包含了頁面中全部的 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 的內容被繪製的那一刻。數組

paint
但實際上咱們有的時候並不須要全部資源的請求時間數據,咱們只關心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)
      }
    }
}
複製代碼

關於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)
      }
    })
  });
  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
複製代碼

參考資料

相關文章
相關標籤/搜索