eruda-timing / src /timing.js
soiz1's picture
Update src/timing.js
64b4000 verified
import ready from 'licia/ready'
import isFn from 'licia/isFn'
import $ from 'licia/$'
import { getFileName } from './util'
export default function(eruda) {
let { evalCss } = eruda.util
class Timing extends eruda.Tool {
constructor() {
super()
this.name = 'timing'
this.displayName = 'タイミング'
this._style = evalCss(require('./style.scss'))
this._performanceTimingData = []
this._performanceTiming = {}
this._showPerformanceDetail = false
this._resourceTimingData = []
this._tpl = require('./template.hbs')
let performance = (this._performance =
window.webkitPerformance || window.performance)
this._hasResourceTiming = performance && isFn(performance.getEntries)
}
init($el, container) {
super.init($el, container)
this._container = container
this._bindEvent()
}
show() {
super.show()
this._render()
}
hide() {
super.hide()
}
destroy() {
super.destroy()
evalCss.remove(this._style)
}
_bindEvent() {
let $el = this._$el,
container = this._container
let self = this
$el
.on('click', '.eruda-performance-timing', function() {
self._showPerformanceDetail = !self._showPerformanceDetail
self._render()
})
.on('click', '.eruda-entry', function() {
let idx = $(this).data('idx'),
data = self._resourceTimingData[Number(idx)]
if (data.initiatorType === 'img') {
showSources('img', data.url)
}
})
.on('click', '.eruda-refresh-resource-timing', () => {
this._render()
})
function showSources(type, data) {
let sources = container.get('sources')
if (!sources) return
sources.set(type, data)
container.showTool('sources')
}
}
_getPerformanceTimingData() {
let performance = this._performance
if (!performance) return
let timing = performance.timing
if (!timing) return
let data = []
/* eslint-disable no-unused-vars */
let {
navigationStart,
unloadEventStart,
unloadEventEnd,
redirectStart,
redirectEnd,
fetchStart,
domainLookupStart,
domainLookupEnd,
connectStart,
connectEnd,
secureConnectionStart,
requestStart,
responseStart,
responseEnd,
domLoading,
domInteractive,
domContentLoadedEventStart,
domContentLoadedEventEnd,
domComplete,
loadEventStart,
loadEventEnd
} = timing
let start = navigationStart,
end = loadEventEnd,
ready = true,
total = end - start
function getData(name, startTime, endTime) {
let duration = endTime - startTime
if (duration < 0) ready = false
return {
name: name,
start: ((startTime - start) / total) * 100,
duration: duration,
len: (duration / total) * 100
}
}
data.push(getData('合計', navigationStart, loadEventEnd))
data.push(getData('ネットワーク/サーバー', navigationStart, responseStart))
data.push(getData('アプリキャッシュ', fetchStart, domainLookupStart))
data.push(getData('DNS', domainLookupStart, domainLookupEnd))
data.push(getData('TCP', connectStart, connectEnd))
data.push(getData('最初のバイトまでの時間', requestStart, responseStart))
data.push(getData('レスポンス', responseStart, responseEnd))
data.push(getData('アンロード', unloadEventStart, unloadEventEnd))
data.push(getData('DOM処理', domLoading, domComplete))
data.push(getData('DOM構築', domLoading, domInteractive))
if (!ready) return
this._performanceTimingData = data
let performanceTiming = {}
;[
'navigationStart',
'unloadEventStart',
'unloadEventEnd',
'redirectStart',
'redirectEnd',
'fetchStart',
'domainLookupStart',
'domainLookupEnd',
'connectStart',
'connectEnd',
'secureConnectionStart',
'requestStart',
'responseStart',
'responseEnd',
'domLoading',
'domInteractive',
'domContentLoadedEventStart',
'domContentLoadedEventEnd',
'domComplete',
'loadEventStart',
'loadEventEnd'
].forEach(val => {
performanceTiming[val] = timing[val] === 0 ? 0 : timing[val] - start
})
this._performanceTiming = performanceTiming
}
_getResourceTimingData() {
if (!this._hasResourceTiming) return
let entries = this._performance.getEntries(),
data = []
let totalTime = 0
entries.forEach(entry => {
if (entry.entryType !== 'resource') return
if (entry.responseEnd > totalTime) totalTime = entry.responseEnd
})
entries.forEach(entry => {
if (entry.entryType !== 'resource') return
let timeline = {
left: (entry.startTime / totalTime) * 100,
connection:
((entry.requestStart - entry.startTime) / totalTime) * 100,
ttfb: ((entry.responseStart - entry.requestStart) / totalTime) * 100,
response:
((entry.responseEnd - entry.responseStart) / totalTime) * 100
}
data.push({
name: getFileName(entry.name),
displayTime: Math.round(entry.duration) + 'ms',
url: entry.name,
timeline,
initiatorType: entry.initiatorType
})
})
this._resourceTimingData = data
}
_render() {
if (!this.active) return
this._getResourceTimingData()
let renderData = { entries: this._resourceTimingData }
if (this._performanceTimingData.length === 0) {
ready(() => {
this._getPerformanceTimingData()
this._render()
})
} else {
this._getPerformanceTimingData()
}
renderData.data = this._performanceTimingData
renderData.timing = this._performanceTiming
renderData.showPerformanceDetail = this._showPerformanceDetail
if (!renderData.timing && !renderData.entries) {
renderData.notSupported = true
}
this._renderHtml(this._tpl(renderData))
}
_renderHtml(html) {
if (html === this._lastHtml) return
this._lastHtml = html
this._$el.html(html)
}
}
return new Timing()
}