灯塔
根据谷歌对灯塔“速度指数”审计的描述:
Lighthouse 使用名为Speedline的节点模块来生成速度指数分数。
发送 Speedline
Speedline 的 Github 自述文件说
WebpageTest.org推出的速度指数旨在解决这个问题。它衡量页面内容在视觉上显示的速度。当前的实现基于Speed Index页面上描述的视频捕获的视觉进度计算方法。通过比较当前帧的直方图与最后一帧的距离来计算视觉进度。
(斜体是我的。)
颜料的时间线
速度指数页面详细介绍了如何计算视觉进度。这是一个片段:
对于基于 Webkit 的浏览器,我们收集时间线数据,其中包括绘制矩形以及其他有用的事件。
我相信“时间线数据”是指通过Performance Timeline API检索到的 JSON 对象。
似乎 Lighthouse 将 JSON 时间线传递给 Speedline,然后提取一组“帧”,描述页面加载的绘制事件:
/**
* @param {string|Array<TraceEvent>|{traceEvents: Array<TraceEvent>}} timeline
* @param {Options} opts
*/
function extractFramesFromTimeline(timeline, opts) {
计算直方图
Speedline 将来自每个绘制事件的图像数据转换为图像直方图,有趣的是排除了“足够接近”以作为白色传递的像素:
/**
* @param {number} i
* @param {number} j
* @param {ImageData} img
*/
function isWhitePixel(i, j, img) {
return getPixel(i, j, 0, img.width, img.data) >= 249 &&
getPixel(i, j, 1, img.width, img.data) >= 249 &&
getPixel(i, j, 2, img.width, img.data) >= 249;
}
很多数学都用于计算和比较直方图。项目维护者是询问这个问题的合适人选。但这是最终确定“视觉完整”的地方:
// find visually complete
for (let i = 0; i < frames.length && !visuallyCompleteTs; i++) {
if (frames[i][progressToUse]() >= 100) {
visuallyCompleteTs = frames[i].getTimeStamp();
}
}
并推断“进展”,
给定帧的“进度”似乎是由这个函数计算的:
/**
* @param {Frame} current
* @param {Frame} initial
* @param {Frame} target
*/
function calculateFrameProgress(current, initial, target) {
let total = 0;
let match = 0;
const currentHist = current.getHistogram();
const initialHist = initial.getHistogram();
const targetHist = target.getHistogram();
for (let channel = 0; channel < 3; channel++) {
for (let pixelVal = 0; pixelVal < 256; pixelVal++) {
const currentCount = currentHist[channel][pixelVal];
const initialCount = initialHist[channel][pixelVal];
const targetCount = targetHist[channel][pixelVal];
const currentDiff = Math.abs(currentCount - initialCount);
const targetDiff = Math.abs(targetCount - initialCount);
match += Math.min(currentDiff, targetDiff);
total += targetDiff;
}
}
let progress;
if (match === 0 && total === 0) { // All images are the same
progress = 100;
} else { // When images differs
progress = Math.floor(match / total * 100);
}
return progress;
}
并且“视觉上完成”是具有 100% 进度的第一帧。
在没有完全审核代码的情况下,我的解释是“视觉上完整的帧”是计算出来的第一帧,其与初始帧的总差异与最终帧的总差异相同(这取决于Lighthouse 选择发送到 Speedline的帧)。
或者,换句话说,它很复杂。