bmfe-monitor
Version:
前端监测平台
232 lines (206 loc) • 8.53 kB
JavaScript
// https://developer.mozilla.org/zh-CN/docs/Web/API/PerformanceTiming
import Report from './report';
let report = new Report()
let cycleFreq = 100; // 循环轮询的时间
let isDOMReady = false;
let isOnload = false;
let performance = window.performance || window.mozPerformance || window.msPerformance || window.webkitPerformance;
let timeFlag = null
let Util = {
addEventListener: function (name, callback, useCapture) {
if (window.addEventListener) {
return window.addEventListener(name, callback, useCapture);
} else if (window.attachEvent) {
return window.attachEvent('on' + name, callback);
}
},
// uninitialized - 还未开始载入
// loading - 载入中
// interactive - 已加载,文档与用户可以开始交互
// complete - 载入完成
domready: function (callback) {
if (isOnload === true) { return void 0; }
let timer = null;
if (document.readyState === 'interactive') {
runCheck();
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', function () {
runCheck();
}, false);
} else if (document.attachEvent) {
document.attachEvent('onreadystatechange', function () {
runCheck();
});
}
function runCheck() {
if (performance.timing.domInteractive) {
clearTimeout(timer);
callback();
isDOMReady = true;
} else {
timer = setTimeout(runCheck, cycleFreq);
}
}
},
onload: function (callback) {
let timer = null;
if (document.readyState === 'complete') {
runCheck();
} else {
Util.addEventListener('load', function () {
runCheck();
}, false);
}
function runCheck() {
if (performance.timing.loadEventEnd) {
clearTimeout(timer);
callback();
isOnload = true;
} else {
timer = setTimeout(runCheck, cycleFreq);
}
}
}
};
// 过滤无效数据
function filterTime(a, b) {
return (a > 0 && b > 0 && (a - b) >= 0) ? (a - b) : undefined;
}
function reportPerf(type,detailShow=false) {
if (!performance) {
return void 0;
}
// append data from window.performance
let timing = performance.timing;
let perfData = {
// 类型
type,
// 网络建连
pervPage: filterTime(timing.fetchStart, timing.navigationStart), // 上一个页面
redirect: filterTime(timing.redirectEnd, timing.redirectStart), // 页面重定向时间
appCache: filterTime(timing.domainLookupStart, timing.fetchStart), // DNS缓存时间
dns: filterTime(timing.domainLookupEnd, timing.domainLookupStart), // DNS查找时间
connect: filterTime(timing.connectEnd, timing.connectStart), // TCP建连时间
network: filterTime(timing.connectEnd, timing.navigationStart), // 网络总耗时
// 网络接收
send: filterTime(timing.responseStart, timing.requestStart), // 从发送到接收到第一个返回
receive: filterTime(timing.responseEnd, timing.responseStart), // 接受页面时间
// 前端渲染
dom: filterTime(timing.domInteractive, timing.domLoading), // dom解析时间
contentLoad: filterTime(timing.domContentLoadedEventEnd, timing.domContentLoadedEventStart), // DOM解析完成后,内置资源加载时间
contentExec: filterTime(timing.domComplete, timing.domContentLoadedEventEnd), // 内置资源执行时间
loadEvent: filterTime(timing.loadEventEnd, timing.loadEventStart), // load执行时间
// 关键阶段
interactive: filterTime(timing.domInteractive, timing.navigationStart), // 可操作时间
ttfb: filterTime(timing.responseStart, timing.navigationStart), // 首字节时间
load: filterTime(timing.loadEventEnd, timing.navigationStart), // 页面完全加载时间
};
if(detailShow) {
let detail = {
navigationStart: timing.navigationStart,
unloadEventStart: timing.unloadEventStart,
unloadEventEnd: timing.unloadEventEnd,
redirectStart: timing.redirectStart,
redirectEnd: timing.redirectEnd,
fetchStart: timing.fetchStart,
domainLookupStart: timing.domainLookupStart,
domainLookupEnd: timing.domainLookupEnd,
connectStart: timing.connectStart,
connectEnd: timing.connectEnd,
secureConnectionStart: timing.secureConnectionStart,
requestStart: timing.requestStart,
responseStart: timing.responseStart,
responseEnd: timing.responseEnd,
domLoading: timing.domLoading,
domInteractive: timing.domInteractive,
domContentLoadedEventStart: timing.domContentLoadedEventStart,
domContentLoadedEventEnd: timing.domContentLoadedEventEnd,
domComplete: timing.domComplete,
loadEventStart: timing.loadEventStart,
loadEventEnd: timing.loadEventEnd,
}
console.table(detail);
}
return perfData;
};
// 获取first-paint及first-contentful-paint的时间
function getPaintTime() {
return new Promise((resolve,reject)=>{
if (window.performance && window.performance.getEntriesByType) {
getWS(resolve)
} else {
reject()
}
})
}
function getWS(resolve) {
clearTimeout(timeFlag)
timeFlag = setTimeout(()=>{
let origin_obj = {}
let result_obj = {}
let paintArr = window.performance.getEntriesByType('paint')
for (const entry of paintArr) {
origin_obj[entry.name] = entry.startTime + entry.duration
}
if (origin_obj['first-paint'] && origin_obj['first-contentful-paint']) {
result_obj['fp'] = Math.round(origin_obj['first-paint'])
result_obj['fcp'] = Math.round(origin_obj['first-contentful-paint'])
result_obj.whiteScreen = Math.round(origin_obj['first-contentful-paint'] - origin_obj['first-paint'])
resolve(result_obj)
} else {
getWS(resolve)
}
},100)
}
function showTable(perfData) {
// console.groupCollapsed(perfData.type)
let data = {
'上一个页面': { '时间(ms)': perfData.pervPage },
'页面重定向': { '时间(ms)': perfData.redirect },
'DNS缓存查找': { '时间(ms)': perfData.appCache },
'DNS查找': { '时间(ms)': perfData.dns },
'TCP建连': { '时间(ms)': perfData.connect },
'网络总耗时': { '时间(ms)': perfData.network },
'从发送到接收到第一个返回': { '时间(ms)': perfData.send },
'接收数据时长': { '时间(ms)': perfData.receive },
'接收首字节用时': { '时间(ms)': perfData.ttfb },
'dom解析': { '时间(ms)': perfData.dom },
'内置资源加载时长': { '时间(ms)': perfData.contentLoad },
'内置资源执行时长': { '时间(ms)': perfData.contentExec },
'load执行用时': { '时间(ms)': perfData.loadEvent },
'页面完全加载': { '时间(ms)': perfData.load },
'白屏': { '时间(ms)': perfData.whiteScreen },
'可操作': { '时间(ms)': perfData.interactive }
}
console.table(data);
// console.groupEnd();
}
let perf = {
init: (config, cb) => {
// Util.domready(() => {
// let perfData = reportPerf('domready');
// cb(perfData);
// });
report.init(config)
Util.onload(() => {
let perfData = reportPerf('onload',config.detailShow);
getPaintTime().then((ptData)=>{
perfData = Object.assign({}, perfData, ptData)
perfData.timestamp = Date.now()
report.send(perfData)
if (config.analyzeShow) {
showTable(perfData)
}
cb && cb(perfData);
},()=>{
perfData.timestamp = Date.now()
report.send(perfData)
if (config.analyzeShow) {
showTable(perfData)
}
cb && cb(perfData);
})
});
}
};
export default perf;