注:只是俺的学习记录
transaction新增网络类型字段
if (navigator && navigator.connection && navigator.connection.effectiveType) {
transaction.addLabels({ 'network_type': navigator.connection.effectiveType });
}
type为resource的span新增文件大小字段、是否命中缓存字段
* transferSize 表示资源传输总大小,包含header,如果资源是从本地缓存中获取的,或者是跨源资源(没有Timing-Allow-Origin HTTP响应标头的资源),此属性返回零。
* encodedBodySize:表示压缩之后的body大小,如果资源是从本地缓存中获取的,或者是跨源资源(没有Timing-Allow-Origin HTTP响应标头的资源),此属性返回零。
* decodedBodySize:表示解压之后的body大小,如果资源是从本地缓存中获取的,或者是跨源资源(没有Timing-Allow-Origin HTTP响应标头的资源),此属性返回零。
以上信息可得知:
跨域资源:transferSize === 0 && encodedBodySize === 0 && decodedBodySize === 0
缓存资源:transferSize === 0 && (encodedBodySize !== 0 || decodedBodySize !== 0)
for(var i = 0; i < spanList.length; i++) { // spanList从transaction里拿
if(spanList[i].type === 'resource') {
if(spanList[i].context && spanList[i].context.http && spanList[i].context.http.response) {
var target_span_res = spanList[i].context.http.response;
if(target_span_res.transfer_size !== 'undefined' && target_span_res.encoded_body_size !== 'undefined' && target_span_res.decoded_body_size !== 'undefined') {
spanList[i].addLabels({
'transfer_size': target_span_res.transfer_size,
'encoded_body_size': target_span_res.encoded_body_size,
'decoded_body_size': target_span_res.decoded_body_size,
'cross_origin_resource': target_span_res.transfer_size === 0 && target_span_res.encoded_body_size === 0 && target_span_res.decoded_body_size === 0,
'hit_cache': target_span_res.transfer_size === 0 && (target_span_res.encoded_body_size !== 0 || target_span_res.decoded_body_size !== 0)
});
}
}
}
}
新增页面滚动时的fps字段
- fps计算: fps为每秒传输帧数,而requestAnimationFrame一般多用来实现连贯的逐帧动画,那么我们计算1s内requestAnimationFrame被调用了多少次,就能得出每秒传输帧数。
// 开始记录fps
var lastTime = null;
var animationId = null;
function startFps() {
lastTime = performance.now(); // 记录开始执行requestAnimationFrame前的时间
var loop = function () {
var now = performance.now();
timeList.push(now); // 记录每次调用requestAnimationFrame的时间
if(!fpsStop) {
animationId = window.requestAnimationFrame(loop);
} else {
window.cancelAnimationFrame(animationId);
}
}
animationId = window.requestAnimationFrame(loop);
}
// 滚动结束后,计算fps
function calculateFps() {
// 1000ms内执行了多少次loop事件
var frame = 0;
for(var i = 0; i < timeList.length; i++) {
frame++;
if (timeList[i] > 1000 + lastTime) { // 执行了1000ms之后
var fps = Math.round( ( frame * 1000 ) / ( timeList[i] - lastTime ) ); // 1000ms运行了多少次
frame = 0;
lastTime = timeList[i];
fpsList.push(fps)
};
}
}
2、页面开始滚动时开始记录fps,并上报到apm
// 创建fpsTransaction
var fpsTransaction = apm.startTransaction('page-scroll-fps', 'user-interaction');
// 停止记录fps
function handleStopFps() {
if(!fpsStop) {
fpsStop = true;
// 计算最后的fps数组
calculateFps();
if(fpsList.length > 0) {
// 拿到fpsList并上报,要先创建fpsTransaction
fpsTransaction.addLabels({ 'fps_list': fpsList });
fpsTransaction.end();
}
}
}
// 处理滚动事件
function handleScroll() {
window.removeEventListener('scroll', handleScroll); // 刚开始滚动就取消监听,下面事件执行一次
// 开始记录fps
startFps();
// 6s后停止记录fps
setTimeout( function() {
handleStopFps();
}, 6000);
}
// scroll事件触发fps开始记录
window.addEventListener('scroll', handleScroll);
完整代码
在这之前要初始化elasticApm
(function () {
var fpsStop = false;
var fpsList = [];
var timeList = [];
var lastTime = null;
var animationId = null;
if(typeof window === 'undefined' || !elasticApm) {
return;
}
// 新建上报fps的transaction
var fpsTransaction = elasticApm.startTransaction(name, type);
// 新增网络类型、文件大小、是否命中缓存上报
elasticApm.observe('transaction:end', function (transaction) {
if(transaction.type === 'page-load') {
var spanList = transaction && transaction.spans.length ? transaction.spans : [];
// https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/connection
// navigator.connection.effectiveType 获取设备的网络连接信息
// 兼容性:pc不兼容火狐、ie、safari;m不支持safari
if (navigator && navigator.connection && navigator.connection.effectiveType) {
transaction.addLabels({ 'network_type': navigator.connection.effectiveType });
}
/**
* https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/transferSize
* https://developer.mozilla.org/en-US/docs/Web/API/Resource_Timing_API#resource_loading_timestamps
* 兼容性:pc不支持ie、safari;m不支持safari
* transferSize 表示资源传输总大小,包含header,如果资源是从本地缓存中获取的,或者是跨源资源(没有Timing-Allow-Origin HTTP响应标头的资源),此属性返回零。
* encodedBodySize:表示压缩之后的body大小,如果资源是从本地缓存中获取的,或者是跨源资源(没有Timing-Allow-Origin HTTP响应标头的资源),此属性返回零。
* decodedBodySize:表示解压之后的body大小,如果资源是从本地缓存中获取的,或者是跨源资源(没有Timing-Allow-Origin HTTP响应标头的资源),此属性返回零。
*/
for(var i = 0; i < spanList.length; i++) {
if(spanList[i].type === 'resource') {
if(spanList[i].context && spanList[i].context.http && spanList[i].context.http.response) {
var targetSpanRes = spanList[i].context.http.response;
if(targetSpanRes.transfer_size !== 'undefined' && targetSpanRes.encoded_body_size !== 'undefined' && targetSpanRes.decoded_body_size !== 'undefined') {
spanList[i].addLabels({
'transfer_size': targetSpanRes.transfer_size,
'encoded_body_size': targetSpanRes.encoded_body_size,
'decoded_body_size': targetSpanRes.decoded_body_size,
'cross_origin_resource': targetSpanRes.transfer_size === 0 && targetSpanRes.encoded_body_size === 0 && targetSpanRes.decoded_body_size === 0,
'hit_cache': targetSpanRes.transfer_size === 0 && (targetSpanRes.encoded_body_size !== 0 || targetSpanRes.decoded_body_size !== 0),
});
}
}
}
}
}
});
// 开始记录fps
function startFps() {
lastTime = performance.now();
var loop = function () {
var now = performance.now();
timeList.push(now);
if(!fpsStop) {
animationId = window.requestAnimationFrame(loop);
} else {
window.cancelAnimationFrame(animationId);
}
};
animationId = window.requestAnimationFrame(loop);
}
// 滚动结束后,计算fps
function calculateFps() {
// 1000ms内执行了多少次loop事件
var frame = 0;
for(var i = 0; i < timeList.length; i++) {
frame++;
if (timeList[i] > 1000 + lastTime) { // 执行了1000ms之后
var fps = Math.round((frame * 1000) / (timeList[i] - lastTime)); // 1000ms运行了多少次
frame = 0;
lastTime = timeList[i];
fpsList.push(fps);
}
}
}
// 停止记录fps
function handleStopFps() {
if(!fpsStop) {
fpsStop = true;
calculateFps();
if(fpsList.length > 0) {
fpsTransaction.addLabels({ 'fps_list': fpsList });
fpsTransaction.end();
}
}
}
// 处理滚动事件
function handleScroll() {
window.removeEventListener('scroll', handleScroll);
startFps();
// 6s后停止记录fps
setTimeout(function () {
handleStopFps();
}, 6000);
}
// scroll事件触发fps开始记录
window.addEventListener('scroll', handleScroll);
})();
参考:
developer.mozilla.org/en-US/docs/…
developer.mozilla.org/en-US/docs/…
developer.mozilla.org/zh-CN/docs/…
fed.taobao.org/blog/taofed…