Что такое FMP?
Возможно, вы знакомы с термином "время белого экрана". Это один из показателей производительности страницы, который мы собирали в эпоху "слэш-энд-огня". При сложности интерфейсной разработки время белого экрана не имеет существенного значения. .На его месте стоит FMP.
Давайте сначала введем некоторые связанные термины.
- FP (First Paint): первая отрисовка, отмечающая момент времени, когда браузер отображает любое содержимое экрана, которое визуально отличается от экрана перед навигацией.
- FCP (First Contentful Paint): первая отрисовка контента, отмечающая время, когда браузер отображает первый контент DOM, который может быть текстом, изображениями, SVG или
<canvas>и т.д. элементы - FMP (First Meaning Paint): первый эффективный рисунок, отмечающий момент времени, когда рендеринг элемента главного героя завершен, элемент главного героя может быть элементом управления видео на веб-сайте с видео, рамка страницы веб-сайта с контентом также может быть заглавное изображение сайта ресурса и т. д.
По сравнению с FP и FCP, FMP является наиболее важным показателем производительности, который больше всего беспокоит наш интерфейс. Google определяет его как момент времени «полезно ли это?». Однако «полезен ли он?» сложно определить в общем виде, поэтому до сих пор нет стандартного вывода API.
Часто существует несколько способов расчета «относительно точного» FMP в сообществе, так называемая относительная точность относительно реального проекта.
- Активная отчетность: разработчик сообщает о времени в позиции «Значение» соответствующей страницы.
- Расчет веса: в соответствии с элементами страницы рассчитайте время рендеринга элемента с наибольшим весом.
- Расчет тренда: Рассчитайте значение FMP в соответствии с тенденцией изменения dom в течение периода рендеринга.
В этой статье речь пойдет о втором способе.
Позиционирование веса
Так называемый вес, то есть переход элементов страницы к определенному DOM или группе DOM с наибольшим «значением веса» в соответствии с согласованным «соотношением веса», а затем использовать его «момент времени загрузки» или «конечная точка загрузки» как отображение FMP.
Расчет веса
тег узла
Если вы хотите пометить узлы DOM поэтапно, у вас должна быть возможность отслеживать изменения DOM, к счастью, HTML5 дает нам такую возможность.
MutationObserver, замену функции Mutation Events, которая является частью спецификации DOM3 Events. Он может выполнять обратные вызовы при изменении указанного DOM.
MutationObserver имеет три метода
-
disconnect()Блокирует уведомление, которое экземпляр MutationObserver продолжает получать до тех пор, пока его методObserver() не будет вызван снова, и функция обратного вызова, содержащаяся в объекте-наблюдателе, больше не будет вызываться.
-
observe()Настройте MutationObserver, чтобы он начал получать уведомления через функцию обратного вызова, когда изменения DOM соответствуют заданным параметрам.
-
takeRecords()Удаляет все ожидающие уведомления из очереди уведомлений MutationObserver и возвращает их в новый массив объектов MutationRecord.
global.mo = new MutationObserver(() => {
/* callback: DOM 节点设置阶段性标记 */
});
/**
* mutationObserver.observe(target[, options])
* target - 需要观察变化的 DOM Node。
* options - MutationObserverInit 对象,配置需要观察的变化项。
* 更多 options 的介绍请参考 https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserverInit#%E5%B1%9E%E6%80%A7
**/
global.mo.observe(document, {
childList: true, // 监听子节点变化(如果subtree为true,则包含子孙节点)
subtree: true // 整个子树的所有节点
});
На следующем рисунке показан грубый анализ процесса рендеринга обычной отдельной страницы.
- Подготовительный этап: Навигационный этап, в процессе подключения соответствующих
- Этап 1: первый этап рендеринга байтов также является первым эффективным изменением дерева FCP и DOM.
- Этап 2: Базовый рендеринг кадра завершен.
- Этап 3: получите данные и визуализировать его на вид
- Фаза IV: загружено изображение, процесс загрузки не отмечен
На самом деле, между первой и третьей стадией еще очень много изменений DOM, срабатывание события Mutation Observer происходит не синхронно, а асинхронно, то есть до тех пор, пока не закончатся все DOM-операции в текущей «стадии».
Mutation Observer имеет следующие особенности
- Он ожидает завершения всех задач сценария перед запуском (т. е. асинхронный запуск).
- Он инкапсулирует записи изменений DOM в массив для обработки, а не обрабатывает изменения DOM по одному.
- Он может наблюдать за всеми типами изменений в DOM, или вы можете указать только определенный тип изменений.
существуетloadПосле запуска события тег каждого этапа был отмечен на теге
Здесь с "_ti』Отметьте ключ прошлой ночью.
При маркировке необходимо записать текущий узел времени для резервного копирования
// 伪代码
function callback() {
global.timeStack[++_ti] = performance.now(); // 记时间
doTag(_ti); // 打标记
}
После разметки дождитесь расчета момента нагрузки и вдавите обратно.
Рассчитать значение веса
Вообще говоря
- Элемент с большей долей просмотра, скорее всего, будет главным элементом.
- Видео с большей вероятностью будут главными героями, чем изображения
-
svgа такжеcanvasтакже важно - Другие элементы можно рассчитать по обычному дом
- Фоновое изображение зависит от ситуации, можете вы его запомнить или нет
Первый шаг: простой и грубый, по размеру
// 伪代码
function weightCompute(node){
let {
width,
height,
left,
top
} = node.getBoundingClientRect();
// 排除视图外的元素
if(isOutside(width, height, left, top)){
return 0;
}
let wts = TAG_WEIGHT_MAP[node.tagName]; // 约定好的权重比
let weight = width * height * wts; // 直接乘,或者更细粒度的计算 wts(width, height, wts)
return {
weight,
wts,
tagName: node.tagName,
ti: node.getAttribute("_ti"),
node
};
}
Шаг вторая: свинец деривация в зависимости от веса
В соответствии с нашим согласованным алгоритмом взвешивания элемент с наибольшим весом является главным элементом, который мы продвигаем.
// 伪代码
function getCoreNode(node){
let list = nodeTraversal(node); // 递归计算每个标记节点的权重值
return getNodeWithMaxWeight(list); // weight 最大的元素
}
Шаг 3: Потратьте время в соответствии с типом элемента
Разные элементы получают время по-разному
- Нормальные элементы: рассчитываются по времени маркера
- Картинки и видео: Рассчитывается по соответствующему времени окончания ресурса.
- С фоновыми элементами: его можно рассчитать в соответствии с соответствующим временем окончания фонового ресурса или его можно рассчитать в соответствии с обычными элементами.
// 伪代码
function getFMP(){
let coreObj = getCoreNode(document.body),
fmp = -1;
let {
tagName,
ti,
node
} = coreObj;
switch(tagName){
case 'IMG':
case 'VIDEO':
let source = node.src;
let { responseEnd } = performance.getEntries().find(item => item.name === source);
fmp = responseEnd || -1;
break;
default:
if(node.style.backgroundImage){
// 普通元素的背景处理
}else{
fmp = global.timeStack[+ti];
}
}
return fmp;
}
Верификация регрессии
Возьмите нашу демонстрационную страницу в качестве примера. Для аналогичных веб-сайтов электронной коммерции мы хотим получить момент времени «Фаза 2» или «Фаза 3» в качестве значения FMP.
Поскольку мы не хотим, чтобы фон «элемента главного героя» или соответствующее время «элемента главного героя изображения» учитывались в значении FMP, мы понижаем рейтинг элементов ресурсов, таких как «изображение» и «видео». к обычным элементам для расчета.
Мы проводим проверку симуляции в условиях Chrome [ Disable cache / Fast 3G ].
Расчетное значение FMP составляет 4730,7 мс, значение, отслеживаемое Chrome Performance, составляет около 4950 мс, а ошибка составляет около 200 мс.
Мы надеемся, что если ограничение скорости будет снято, значение FMP будет ближе к «First Meaning Paint».
Перепечатка с указанием источника
Автор: деревянное перо zwwill
Первый адрес:zwwill/blog#32