1. 程式人生 > 其它 >首屏載入時間計算

首屏載入時間計算

文章:https://cloud.tencent.com/developer/article/1850013

function getFirstScreenTime() {
const details = [];
return new Promise(function (resolve, reject) {
// 5s之內先收集所有的dom變化,並以key(時間戳)、value(dom list)的結構存起來。

const observeDom = observeDoc(details);

setTimeout(function () {
observeDom.disconnect();
console.log('details', details);
resolve(details);
}, 5000);
}).then(function (details) {
return compareTime(details);
});
}

// 監聽文件
function observeDoc(details) {
var observeDom = new MutationObserver(function (mutations) {
if (!mutations || !mutations.forEach) return;
var detail = {
time: performance.now(),
roots: [],
imgs: [],
};

mutations.forEach(function (mutation) {
if (!mutation || !mutation.addedNodes || !mutation.addedNodes.forEach) {
return;
}
mutation.addedNodes.forEach(function (ele) {
const nodeName = ele.nodeName.toLocaleLowerCase();
const isImg = nodeName === 'img';
const isValidEle = isEleValid(ele, detail.roots);
if (isValidEle) {
detail.roots.push(ele);
if (isImg) {
detail.imgs.push(ele);
}
}
});
});
if (detail.roots.length) {
details.push(detail);
}
});
observeDom.observe(document, {
childList: true,
subtree: true,
});
return observeDom;
}

// 比較首屏內DOM時間
function compareTime(details) {
// 分析上面收集到的資料,返回最終的結果
const domRenderTime = [];
const allImgs = [];
const allImgLoadTimes = [];

details.forEach(function (detail) {
// 拿到所有首屏DOM 的 渲染時間
for (var i = 0; i < detail.roots.length; i++) {
const ele = detail.roots[i];
if (isInFSAndVisible(ele)) {
domRenderTime.push(detail.time);
// 同一個 detail 的 節點,時間一樣,不用繼續收集了
break;
}
}

// 拿到所有首屏中的圖片
for (var i = 0; i < detail.imgs.length; i++) {
const ele = detail.imgs[i];
if (isInFSAndVisible(ele)) {
allImgs.push(ele.src);
}
}
});

// 遍歷當前請求的圖片中,如果有開始請求時間在首屏dom渲染期間的,則表明該圖片是首屏渲染中的一部分,
// 所以dom渲染時間和圖片返回時間中大的為首屏渲染時間
window.performance.getEntriesByType('resource').forEach(function (resource) {
const url = resource.name;
if (resource.initiatorType === 'img' && allImgs.indexOf(url) > -1) {
allImgLoadTimes.push(resource.responseEnd);
}
});

console.log('allImgs', allImgs);
console.log('domRenderTime', domRenderTime);
console.log('allImgLoadTimes', allImgLoadTimes);

const resultTime = Math.max(...domRenderTime, ...allImgLoadTimes);

return resultTime;
}

// 節點是否有效
function isEleValid(ele, arr) {
const ignoreEleList = ['script', 'style', 'link', 'br'];
const nodeName = ele.nodeName.toLocaleLowerCase();
const isEleNode = ele.nodeType === 1;
const isRenderNode = ignoreEleList.indexOf(nodeName) !== -1;
if (isEleNode && isRenderNode) {
if (isInFS(ele)) {
if (!isElderOrExitEle(ele, arr)) {
return true;
}
}
}
return false;
}

// dom 是否有效,過濾
function isEleValid(ele, arr) {
const ignoreEleList = ['script', 'style', 'link', 'br'];
const nodeName = ele.nodeName.toLocaleLowerCase();
const isEleNode = ele.nodeType === 1;
const isRenderNode = ignoreEleList.indexOf(nodeName) === -1;
if (isEleNode && isRenderNode) {
if (isInFS(ele)) {
if (!isElderOrExitEle(ele, arr)) {
return true;
}
// 圖片另算
else if (nodeName === 'img') {
return true;
}
}
}
return false;
}

// 是否已經存在節點,或者是長輩節點
function isElderOrExitEle(target, arr) {
if (!target || target === document.documentElement) {
return false;

// 說明陣列已經存在這個節點
} else if (arr.indexOf(target) !== -1) {
return true;

// 不要長輩節點
} else {
return isElderOrExitEle(target.parentElement, arr);
}
}

// 位置是否首屏中
function isInFS(target) {
if (!target || !target.getBoundingClientRect) return false;
var rect = target.getBoundingClientRect(),
screenHeight = window.innerHeight,
screenWidth = window.innerWidth;
return (
rect.left >= 0 &&
rect.left < screenWidth &&
rect.top >= 0 &&
rect.top < screenHeight
);
}

// 首屏中,並且可見
function isInFSAndVisible(target) {
if (!target || !target.getBoundingClientRect) return false;
var rect = target.getBoundingClientRect();
const isvisible = isVisible(target);
const hasArea = rect.width > 0 && rect.height > 0;

return isInFS(target) && hasArea && isvisible;
}

// 是否可見
function isVisible(target) {
target.style.opacity = 'inherit';
var rawOpacity = getStyle(target, 'opacity');
var visibility = getStyle(target, 'visibility');

var opacity = +rawOpacity;
if (opacity != rawOpacity) {
opacity = 1; // can not check old browser
}
if (opacity > 0.09 || visibility === 'visible') {
return true;
}
return false;
}

// 獲取樣式
function getStyle(el, name) {
if (window.getComputedStyle) {
return getComputedStyle(el, null)[name];
}
if (el.currentStyle) {
return el.currentStyle[name];
}
}