Spaces:
Sleeping
Sleeping
| 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() | |
| } | |