На 31 июля 2026 года строгий аудит проходит 148 из 358 созданных материалов. Остальные 210 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить.
На 31 июля 2026 года строгий аудит проходит 151 из 358 созданных материалов. Остальные 207 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить.
Скрипт экспортирует только `revisions`. В каждом overlay-объекте есть только
поля публикации; `date` и `author` отсутствуют. `--print-revisions` печатает
тот же import-safe массив, а`--verify-fixture` запускает локальную модель.
## Историческая рамка и источники
Материал ограничен мартом 2022 года. Проверены три первичных или официальных
источника с датированным либо неизменяемым URL:
- [WHATWG HTML, immutable commit 9d42d2c от 27 февраля 2022 года](https://github.com/whatwg/html/commit/9d42d2ca8523758893cdc8d7de6e0815e4178b81);
- [W3C Timing control for script-based animations, Working Group Note от 22 сентября 2015 года](https://www.w3.org/TR/2015/NOTE-animation-timing-20150922/);
- [W3C Performance Timeline Level 2, Working Draft от 19 августа 2021 года](https://www.w3.org/TR/2021/WD-performance-timeline-2-20210819/).
HTTP preflight всех трёх URL завершился `200`. Из источников взяты только
узкие факты: rendering обновляется user agent, `requestAnimationFrame`
запрашивает планирование animation frame update, а Performance Timeline
описывает работу с performance entries. Они не устанавливают единственный
browser pipeline, не задают labels fixture и не дают числовой бюджет. В статьях
нет INP, поздних интерфейсных возможностей DevTools, современных рекомендаций
по Web Vitals или утверждений о production-метриках.
## Проход 1 — факты и техника
Проверял: границу между документированным поведением, реальной записью
Performance panel и учебной моделью.
`planRenderFrame` и `runBrowserRenderingFixture` используют только локальные
object/array. В них нет DOM, browser API, `requestAnimationFrame`, сети,
таймера, файлов, CPU profile, trace или Web Vitals. Значения `units` всегда
названы `synthetic-work-units`; это не milliseconds, duration, FPS, CPU или
<titleid="title">Диагностика DOM-изменения с остановками и rollback</title>
<descid="desc">Блок-схема проверяет DOM result is-pending в is-ready. Если контракт неверен или изменение no-op, расследование останавливается. Валидный путь выбирает одну interaction и вопросы к pipeline. Пересечение учебной границы ведёт к одной обратимой гипотезе и rollback, который возвращает is-pending отдельным кадром.</desc>
<textx="56"y="66"fill="#f8fafc"font-family="Arial, sans-serif"font-size="36"font-weight="700">Диагностика: остановить ложный вывод раньше правки</text>
<rectx="330"y="456"width="344"height="88"rx="14"fill="#5f3337"stroke="#ff867c"stroke-width="2"/><textx="357"y="492"fill="#ffe4e1"font-weight="700">no-op → stop</text><textx="357"y="522"fill="#ffe4e1">нет нового DOM result</text>
<textx="70"y="692"fill="#b8c8e0"font-family="Arial, sans-serif"font-size="18">Все units synthetic. Схема не является trace, benchmark, milliseconds или доказательством jank пользователя.</text>
<titleid="title">Учебная pipeline от DOM contract к вопросам для записи</title>
<descid="desc">Пять последовательных блоков JS mutation, style, layout, paint и composite. Под каждым блоком указан вопрос к выбранному диапазону записи. Пунктирная рамка отмечает, что это модель, а не обязательные имена событий браузера.</desc>
<textx="60"y="70"fill="#f3fbff"font-family="Arial, sans-serif"font-size="36"font-weight="700">Не «виновник», а последовательность вопросов</text>
<textx="60"y="108"fill="#b8d6e8"font-family="Arial, sans-serif"font-size="22">DOM contract сначала, trace range потом, изменение — только после гипотезы</text>
<textx="72"y="180"fill="#8ed6ff"font-family="Arial, sans-serif"font-size="20">LABELS УЧЕБНОЙ МОДЕЛИ, НЕ ОБЯЗАТЕЛЬНЫЕ EVENT NAMES</text>
<gfont-family="Arial, sans-serif">
<rectx="70"y="230"width="184"height="218"rx="16"fill="#173d57"/><textx="91"y="276"fill="#f4fcff"font-size="24"font-weight="700">JS mutation</text><textx="91"y="320"fill="#b9eaff"font-size="19">Что записал</text><textx="91"y="347"fill="#b9eaff"font-size="19">handler?</text><textx="91"y="408"fill="#85d5ff"font-size="20">className X → Y</text>
<rectx="282"y="230"width="184"height="218"rx="16"fill="#195047"/><textx="333"y="276"fill="#effff9"font-size="24"font-weight="700">style</text><textx="303"y="320"fill="#bff4de"font-size="19">Какой selector</text><textx="303"y="347"fill="#bff4de"font-size="19">state изменён?</text><textx="322"y="408"fill="#8de0bb"font-size="20">вопрос к range</text>
<textx="98"y="638"fill="#d4f6e9"font-family="Arial, sans-serif"font-size="24">Факт записи + DOM result → один owner → обратимое действие → повтор того же сценария</text>
<titleid="title">Учебная timeline одного DOM-изменения и граница условной работы</title>
<descid="desc">Нажатие Сохранить меняет className карточки. Далее пять блоков модели: JS mutation, style, layout, paint, composite. Их условные единицы равны 1, 2, 4, 3 и 1, сумма 11 пересекает учебную границу 10. Подпись поясняет, что это не миллисекунды и не trace браузера.</desc>
<rectwidth="1200"height="720"fill="#0d1628"/>
<textx="64"y="72"fill="#f8fafc"font-family="Arial, sans-serif"font-size="36"font-weight="700">Один DOM-result → один учебный кадр</text>
<textx="96"y="550"fill="#ffcf87"font-family="Arial, sans-serif"font-size="25"font-weight="700">jank-boundary — только label учебной модели</text>
<textx="64"y="648"fill="#b8c8e0"font-family="Arial, sans-serif"font-size="22">Не milliseconds · не CPU · не FPS · не Web Vitals · не Performance trace</text>
note:'первичный снимок HTML Standard от 27 февраля 2022 года. Он задаёт алгоритм update the rendering и подчёркивает, что шаги зависят от user agent; он не устанавливает числовой бюджет и не обещает одинаковую последовательность внутренних событий во всех браузерах.',
};
constanimationTimingNote={
title:'W3C Timing control for script-based animations, Working Group Note от 22 сентября 2015 года',
note:'датированная W3C Working Group Note от 22 сентября 2015 года, доступная к марту 2022-го. Она описывает requestAnimationFrame как запрос к user agent запланировать animation frame update; это не Recommendation. Fixture не реализует этот API и не измеряет частоту кадров.',
};
constperformanceTimelineDraft={
title:'W3C Performance Timeline Level 2, Working Draft от 19 августа 2021 года',
note:'датированный первичный W3C draft о performance entries и их получении. Он не описывает labels fixture, не задаёт teaching boundary и не превращает synthetic units в настоящий trace или данные Performance panel.',
excerpt:'Практический маршрут для симптома «страница тормозит»: назвать одно DOM-изменение, собрать одну запись и проверить, где в кадре начинается работа — без подмены модели настоящим trace.',
readingMinutes:12,
},
[
paragraph('Проблема «медленный рендер» обычно скрывает четыре разные работы: JavaScript меняет DOM, движок пересчитывает style, раскладывает boxes, готовит paint и собирает результат. Пока всё это называют одним словом, исправление выбирают наугад: переносят обработчик, меняют CSS или добавляют debounce. Цена — дорогая правка, после которой пользовательский сценарий не стал понятнее, а следующая запись Performance panel всё ещё не отвечает, какое действие создало тяжёлый кадр.'),
paragraph('Начинать стоит не с цветов в панели, а с одного наблюдаемого изменения. Например: нажатие «Сохранить» меняет у карточки <code>className</code> с <code>card is-pending</code> на <code>card is-ready</code>. У этого изменения есть исходный DOM-result, место в обработчике и ожидаемый новый result. Если такой контракт записан, его можно связать с одной interaction в реальной записи и просмотреть события выбранного кадра по порядку. Если контракта нет, trace остаётся длинной лентой, в которой всё похоже на причину.'),
heading('Сначала назвать границу, а не оптимизацию'),
paragraph('Полезная единица работы — «одна DOM-мутация в одном пользовательском действии». Не «вся страница», не «React медленный» и не «CSS тяжёлый». Для выбранной мутации выписываем target, property, old value, new value и способ повторить действие. В статье target — <code>save-card</code>, property — <code>className</code>. Это не универсальная форма для любого приложения. Она нужна, чтобы reviewer мог отличить исходную работу от соседней анимации, сетевого ответа или рендера другого компонента.'),
paragraph('Дальше важно не превратить имена стадий в обещание браузера. HTML Standard описывает update the rendering как алгоритм user agent; его подробности и планирование не дают автору права требовать одинаковый внутренний список событий у всех движков. Поэтому ниже есть учебная модель. Она говорит, какой вопрос задать о результате изменения: была ли JS-мутация, потребовалась ли работа style/layout/paint/composite, где образовалась граница диагностики. Она не говорит, как Chrome, Firefox или Safari обязаны назвать одно событие в trace.'),
dataTable(
'Контракт одного изменения до открытия Performance panel',
['Поле','Пример','Зачем нужно','Чего не доказывает'],
[
['Действие','нажатие «Сохранить»','повторить один сценарий','что все другие пути быстры'],
['DOM before','<code>card is-pending</code>','зафиксировать исходное состояние','что style уже вычислен'],
['DOM after','<code>card is-ready</code>','проверить результат обработчика','что был фактический paint'],
['Граница записи','одна interaction и один frame range','сузить ленту событий','причину без чтения деталей'],
['Следующий вопрос','какая работа следует за mutation','выбрать один объект проверки','готовое архитектурное решение'],
],
),
heading('Учебный контракт кадра'),
paragraph('Fixture пакета не открывает браузер. Она работает с локальными object и array, не вызывает DOM API, <code>requestAnimationFrame</code>, сеть, таймер, profiler или Performance API. Для валидной смены класса она строит пять labels: <code>js-mutation</code>, <code>style</code>, <code>layout</code>, <code>paint</code> и <code>composite</code>. У каждого label есть synthetic work units. Это проектные значения, специально выбранные для теста порядка и границы. Они не являются milliseconds, CPU, FPS, LCP, INP, Web Vitals или данными настоящей записи.'),
codeBlock(fixtureCommand),
paragraph('В forward route сумма равна 11 synthetic units: 1 + 2 + 4 + 3 + 1. Учебная граница равна 10, поэтому fixture отмечает <code>jank-boundary</code>. Это слово здесь не означает dropped frame и не является измерением задержки. Оно означает ровно одно: заранее описанное правило модели пересечено. Такая фиксация полезна для примера, потому что тест может проверить условие, а текст не обещает скорость устройства, которой никто не измерял.'),
'Учебная timeline одного изменения: нажатие меняет className карточки с is-pending на is-ready. Затем показаны пять стадий модели — JS mutation, style, layout, paint и composite — с synthetic units 1, 2, 4, 3, 1. Сумма 11 пересекает учебную jank-boundary 10. Рядом явно написано, что это не milliseconds и не trace браузера.',
'Timeline задаёт порядок вопроса для диагностики. Она не рисует и не измеряет настоящий кадр Performance panel.',
),
heading('Как связать изменение с реальной записью'),
paragraph('В настоящем проекте маршрут короче, чем кажется. Откройте страницу в согласованном состоянии, выполните только выбранное действие и остановите recording. Затем найдите interaction по времени и выделите узкий диапазон вокруг неё. Сначала вернитесь к коду: действительно ли обработчик меняет тот DOM-result, который записан в контракте? Затем читайте подробности выбранного диапазона, а не всю ленту. Если изменение оказалось другим, не переносите вывод с учебной схемы: обновите контракт или возьмите новый scenario.'),
orderedList([
'<strong>Симптом.</strong> После конкретного действия экран отвечает неровно, но «медленный рендер» не указывает место в коде.',
'<strong>Причина.</strong> В одну причину смешаны DOM mutation, style, layout, paint и JavaScript до них.',
'<strong>Проверка входа.</strong> Запишите target, property, before, after и один способ повторить действие. Убедитесь в DevTools Elements, что after действительно появился.',
'<strong>Проверка записи.</strong> Соберите одну interaction, выделите её диапазон и найдите работу, связанную с этим действием. Не называйте учебные labels буквальными именами trace events.',
'<strong>Действие.</strong> Выберите один следующий объект: handler, CSS rule, geometry read или paint boundary. Подготовьте одну обратимую правку.',
'<strong>Повтор.</strong> Снова выполните тот же контракт. Если DOM result изменился, старую запись нельзя использовать как доказательство нового поведения.',
]),
heading('Где часто теряется причина'),
paragraph('Первый сбой — брать запись после серии кликов. В ней соседствуют несколько mutations, timers, network callbacks и события интерфейса. Даже если внутри есть длинный участок, привязать его к одной строке кода уже трудно. Второй сбой — смотреть только итоговую шкалу и не проверять DOM after. Тогда мы не знаем, рисовали ли именно ожидаемый элемент или обработчик вообще выбрал другую ветку. Третий — считать, что style, layout и paint всегда существуют как отдельные и одинаково подписанные карточки. Это удобная ментальная модель, но не API контракта между приложением и браузером.'),
paragraph('Сильнее работает обратная формулировка: «после className X → Y я хочу понять, какая работа возникла в выбранной записи и можно ли убрать её без изменения result». Она запрещает лечить симптом удалением видимого эффекта. Если карточка должна стать <code>is-ready</code>, действие не считается успешным, пока after-state не сохранён. Производительность и функциональный result проверяются вместе, а не в разных спорящих задачах.'),
heading('Ограничения модели'),
paragraph('Учебный порядок из пяти labels не раскрывает все случаи. Например, изменение может не требовать layout, браузер может отложить часть работы, а compositing зависит от свойств и движка. Модель также не содержит изображений, шрифтов, iframe, анимации, cache, процессора или расширений. Поэтому она не отвечает на вопрос «какой CSS property всегда дешёвый» и не годится для сравнения устройств. Её единственная роль — удержать связь между declared DOM result, порядком проверки и обратимым действием.'),
paragraph('Источники ниже существуют до марта 2022 года: immutable HTML snapshot от 27 февраля, датированная W3C Working Group Note для animation timing и W3C draft Performance Timeline 2021 года. Они помогают не выдать модель за стандартную шкалу или trace. Ни один из них не задаёт synthetic units и boundary fixture. Перед переносом на приложение зафиксируйте версию браузера, сценарий и способ записи отдельно: это уже свойства реального исследования, которых в пакете намеренно нет.'),
heading('Следующий проверяемый шаг'),
paragraph('Возьмите один реальный button или input, составьте таблицу before/after и сделайте единственную запись вокруг одного действия. Результатом следующего шага должен стать не «страница быстрее», а проверяемый артефакт: ссылка на handler, DOM-result и один выделенный диапазон trace. Затем внесите обратимое изменение и повторите тот же сценарий. Если after-state сохранён, а связанная работа стала понятнее, диагностика уже стала дешевле — без придуманной production-метрики.'),
excerpt:'Причинная модель для DOM-изменения: отделить JavaScript, style, layout, paint и composite, чтобы не выдавать учебную схему за внутренний контракт браузера.',
readingMinutes:13,
},
[
paragraph('Проблема возникает, когда на карточке меняется класс, а команда спорит, «это JavaScript» или «это CSS». Оба ответа могут быть неполными. JavaScript может только записать className; дальше браузер решает, нужна ли работа со style, геометрией, пикселями и слоями. Цена неверной модели заметна сразу: удаляют часть handler, хотя DOM-result должен остаться, либо переписывают style rule, который не относится к выбранному действию. В итоге меняется код, но причина в Performance panel остаётся неподтверждённой.'),
paragraph('Нужна простая причинная граница: изменение значения — это вход; visual result — это выход; между ними есть работа, которую нужно наблюдать на одной записи. Нельзя честно назвать одинаковый конвейер для каждого браузера и свойства. Но можно не смешивать вопросы. Что изменил handler? Какой result стал виден? Какая работа находится после этого действия в выбранном диапазоне? Какое минимальное обратимое действие проверит гипотезу? Такой порядок даёт техническую речь без лозунга «избегайте layout». '),
heading('Пять labels модели и их ответственность'),
paragraph('В модели <code>js-mutation</code> применяет объявленное изменение <code>className</code>. <code>style</code> означает, что нужно определить применимые стили после нового класса. <code>layout</code> представляет расчёт положения карточки после style. <code>paint</code> представляет подготовку её пикселей. <code>composite</code> представляет сборку подготовленного результата. Это не протокол браузера и не словарь Performance panel. Это пять вопросов, которые удобно держать рядом с DOM contract, чтобы не назвать одну широкую область причиной до исследования.'),
dataTable(
'Причинная модель кадра: что проверяем после DOM mutation',
['Label модели','Вход','Результат модели','Вопрос к записи','Неправильный вывод'],
[
['<code>js-mutation</code>','handler и old/new className','обновлённый DOM object','какой код сделал mutation?','«всё после клика — JavaScript»'],
['<code>style</code>','новый selector state','resolved style в модели','есть ли связанная работа со style?','«class всегда означает одну цену»'],
['<code>layout</code>','style, влияющий на геометрию','placement карточки','какая геометрия стала нужна?','«layout всегда отдельная карточка»'],
['<code>paint</code>','визуальное представление','prepared pixels','какой визуальный участок обновился?','«paint равен CSS rule»'],
['<code>composite</code>','prepared result','собранный frame result','какая финальная работа видна?','«это автоматически дешёвый этап»'],
],
),
paragraph('Слово «модель» здесь делает важную работу. HTML Standard говорит о том, что user agent обновляет rendering, но не обещает авторскому коду доступ к одной неизменной внутренней очереди. Trace format Chromium описывает, как события следа представляются в записи, а не контракт «после className всегда идёт именно такой набор работ». Поэтому между документом, записью и проектным решением есть граница: стандарт и source помогают назвать предмет; запись показывает поведение выбранной версии; решение выбирает изменение и способ его отменить.'),
'Учебная pipeline-схема: DOM contract className is-pending → is-ready входит в JS mutation, затем проходит через style, layout, paint и composite. Под каждым блоком указаны вопрос к выбранному диапазону записи и synthetic work units. Пунктирная рамка предупреждает, что labels не являются обязательными именами браузерных событий.',
'Pipeline разделяет вопросы о причине. Она не утверждает, что любой браузер отдаёт пять одинаковых trace-событий.',
),
heading('Почему порядок важнее суммы'),
paragraph('Fixture ставит units 1, 2, 4, 3 и 1. Их сумма 11 пересекает teaching boundary 10. Числа специально не похожи на duration: это не миллисекунды, не CPU time и не FPS. Важен порядок. Если мы видим, что договорённая mutation не произошла, бессмысленно обсуждать paint. Если mutation есть, но DOM after не тот, сначала исправляется функциональный путь. Если result правильный, можно перейти к следующему вопросу и ограничить гипотезу. Сумма в такой модели нужна только для теста определённой ветки, а не для рейтинга производительности.'),
codeBlock(routeExample),
paragraph('Валидный route проходит строго через пять labels. В fixture проверяется exact order и то, что у каждой стадии есть причина. Это полезно как защита от случайной правки самой модели: нельзя переставить paint перед layout и продолжать рассказывать тот же учебный маршрут. Но fixture не проверяет движок. Её нельзя запускать вместо Performance panel, сравнивать с отчётом Lighthouse или приклеивать к CI как замер страницы. Она проверяет только контракт статьи: что заявленные вход, стадийность, boundary и result не противоречат друг другу.'),
heading('Jank boundary — условие остановки, а не диагноз'),
paragraph('Название <code>jank-boundary</code> здесь намеренно ограничено. Оно срабатывает, когда 11 условных единиц больше 10. Оно не говорит, что пользователь увидел рывок, что кадр пропущен или что устройство не успело выполнить работу. В настоящем исследовании такие выводы требуют настоящей записи, условий запуска и наблюдения конкретного браузера. В учебной модели boundary отвечает на иной вопрос: есть ли заранее определённый случай, в котором автор обязан не продолжать оптимизацию на словах, а перейти к следующей проверке.'),
paragraph('Это похоже на type check. Компилятор не измеряет удобство интерфейса, но останавливает конкретное несоответствие. Здесь ограничение останавливает расплывчатый вывод: «после клика что-то много происходило». Мы получаем ровно: «для declared change модель прошла определённую границу; теперь надо собрать реальный evidence на этой границе». Такая дисциплина не делает браузер предсказуемее, зато не позволяет из красивой схемы сделать выдуманный benchmark.'),
heading('Две ветки, которые должны быть пустыми'),
paragraph('No-op route важен не меньше changed route. Если <code>to</code> совпадает с текущим <code>className</code>, fixture возвращает <code>no-op</code>, не выдаёт frameId и оставляет stages пустыми. В модели это означает: новое действие не объявлено, поэтому конвейер не планируется. Это не утверждение, что реальный браузер никогда не делает никакой работы рядом с одинаковой записью свойства. Это защита контракта: статья не должна изображать кадр, если сама заявляет отсутствие DOM result.'),
paragraph('Error route столь же полезен. Если передан <code>textContent</code> вместо разрешённого <code>className</code>, либо target/from не совпадают с контрактом, модель возвращает <code>invalid-update-contract</code> и также не меняет DOM object. Ошибка не превращается в «нулевой быстрый кадр». Она означает, что у автора нет основания строить причинную историю. В реальной задаче это момент вернуться к handler, selector или тесту UI, а не искать удачный цвет в диаграмме.'),
heading('Маршрут: симптом → причина → проверка → действие'),
orderedList([
'<strong>Симптом.</strong> Один клик меняет интерфейс неровно, а команда называет причину то JS, то CSS.',
'<strong>Причина.</strong> Не записаны mutation и result, поэтому разные стадии и соседняя работа попали в одну историю.',
'<strong>Проверка контракта.</strong> Сверьте target, property, from и to. Если это не className <code>save-card</code>, fixture честно идёт в error route.',
'<strong>Проверка порядка.</strong> Для changed route удерживайте цепочку mutation → style → layout → paint → composite как порядок вопросов, не как имена trace events.',
'<strong>Проверка реальности.</strong> В Performance panel выделите только interaction и сопоставьте её с кодом. Если доменный result другой, начните новый сценарий.',
'<strong>Действие.</strong> Поменяйте один owner: handler, правило style или geometry read. Сохраните способ rollback до повторной записи.',
]),
heading('Обратимость не должна ломать визуальный result'),
paragraph('Проверка улучшения без отмены легко обманывает. Можно убрать className change и увидеть меньше работы, но карточка больше не сообщает готовность. Поэтому fixture создаёт второй frame: rollback меняет <code>card is-ready</code> обратно в <code>card is-pending</code>, получает отдельный <code>teaching-frame-08</code> и проходит тот же порядок labels. Assertion требует, чтобы final DOM равнялся base DOM. Это не browser undo; это минимальная проверка, что мы умеем вернуть исходный функциональный контракт.'),
codeBlock(rollbackExample),
paragraph('В приложении rollback может быть feature flag, локальная правка selector или отдельный commit. Форма не важна, пока она возвращает ожидаемый DOM result и может быть повторена. Важно не смешивать rollback с отсутствием изменения: no-op ничего не проверяет, потому что не создаёт нового result. Обратимое изменение создаёт result, наблюдает его, а затем осознанно возвращает старый. Именно поэтому оно пригодно для исследования, а не только для безопасного релиза.'),
heading('Ограничения и историческая рамка'),
paragraph('Март 2022 — подходящая рамка для разговора о DevTools trace и frame update, но не повод использовать будущие интерфейсные метрики или поздние UI Performance panel. Здесь нет INP, interaction breakdown, современных DevTools labels и обещаний о RAIL. Ссылки зафиксированы до заданного месяца: WHATWG commit от 27 февраля 2022 года, W3C Working Group Note 2015 года и W3C Performance Timeline draft августа 2021-го. Они первичны или официальны, но не превращают эту схему в нормативную декомпозицию render pipeline.'),
paragraph('Следующий проверяемый шаг: добавьте в задачу ровно одну строку «mutation, before, after, owner, rollback». После первой реальной записи оставьте ссылку на выделенный range и короткую гипотезу уровня «проверить geometry read в handler», а не «убрать layout». Если следующий trace не подтверждает связь, вернитесь к контракту. Это быстрее, чем защищать оптимизацию, которая была выбрана до того, как появилось доказательство.'),
],
commonSources,
);
constfieldArticle=createRevision(
{
slug:'editorial-2022-03-field-browser-rendering',
title:'Разбор одного тяжёлого кадра: от DOM result к обратимой проверке',
excerpt:'Полевой маршрут для Performance panel: не лечить «медленный рендер» по цвету, а сузить один DOM-result, одну interaction, один вопрос к trace и обратимую правку.',
readingMinutes:13,
},
[
paragraph('В записи Performance panel легко найти широкий участок работы и ещё легче назвать его «проблемой рендера». Но из такой фразы нельзя получить безопасную правку. Неизвестно, какой пользовательский шаг начался раньше, какой DOM-result был нужен и что сломается, если просто убрать часть кода. Цена — оптимизация, которая делает график спокойнее, но оставляет кнопку в старом состоянии, убирает сообщение об ошибке или переносит работу в другой момент без объяснения.'),
paragraph('Полевой разбор начинается с малого: один input, один DOM-result и одна запись. Для примера пользователь нажимает «Сохранить», а карточка меняет <code>className</code> с <code>card is-pending</code> на <code>card is-ready</code>. Мы не утверждаем, что такой сценарий произошёл в реальном продукте. Это учебный case. Его задача — показать маршрут, где каждое утверждение можно заменить артефактом своего проекта: DOM snapshot, ссылка на handler, выбранный range trace и обратимая правка.'),
heading('Сначала зафиксировать то, что нельзя потерять'),
paragraph('До записи сформулируйте result в одном предложении: «после submit карточка остаётся той же, но className становится is-ready». Сохраните before/after в Elements или в UI-test expectation. Это удерживает границу функциональности. Если следующая правка убирает visual state, она не проходит, даже если trace выглядит лучше. Производительность не является отдельным экраном без результата; пользователь нажал кнопку именно ради изменения интерфейса.'),
['Сценарий','одно действие, исходный экран','другой человек повторяет его','записать длинную серию кликов'],
['DOM contract','target/property/before/after','after виден после действия','считать код handler доказательством результата'],
['Recording range','одна interaction и узкий диапазон','range соотносится со сценарием','читать всю timeline сразу'],
['Гипотеза','один owner и один вопрос','можно опровергнуть следующей записью','объявить виновным весь CSS или framework'],
['Rollback','как вернуть old result','после отмены DOM равен before','путать отмену с no-op'],
],
),
paragraph('Fixture вводит ровно те же поля, но оставляет их учебными. Changed route получает frame id, пять labels и total 11 synthetic work units. Boundary 10 даёт <code>jank-boundary</code>. Никаких duration, CPU, frame rate или реального UI в ней нет. Поэтому нельзя скопировать число 11 в performance ticket. Можно скопировать структуру вопроса: почему после этой mutation в выбранной записи находится работа, какой owner способен её поменять и как проверить, что DOM result не потерян.'),
'Диагностическая схема: сначала проверяется DOM result className is-pending → is-ready. Затем выбирается одна interaction и порядок вопросов JS mutation, style, layout, paint, composite. При пересечении учебной boundary создаётся гипотеза об одном owner и обратимое изменение. Ветка no-op останавливает расследование; rollback возвращает is-pending отдельным кадром. Все units отмечены как synthetic, не trace и не milliseconds.',
'Схема показывает, когда надо остановиться: при неверном DOM result, no-op или invalid contract нельзя делать performance verdict.',
),
heading('Читать запись от действия к коду'),
paragraph('Открыв Performance panel, сначала сузьте время: начните recording на уже подготовленном экране, выполните одно действие и остановите его сразу после visual result. Выберите interaction и найдите связанный участок на main thread. Затем вернитесь в код и спросите, что именно меняет DOM. Не делайте обратное — не берите самый широкий блок и не ищите ему удобное объяснение. У широкого блока могут быть unrelated timers, обработка сети, расширение браузера или соседняя animation. Без связи со сценарием у него нет владельца для правки.'),
paragraph('После связи переходите по одному вопросу. Если handler читает геометрию после записи style, проверяем этот read. Если DOM result появляется раньше, а видимая работа следует позже, проверяем CSS rule и область update. Если mutation вообще не соответствует before/after, не продолжаем investigation: это другой сценарий. Такой порядок не обещает быстрый результат, но экономит время на спорах. Каждый шаг либо сужает причину, либо честно возвращает к отсутствующему артефакту.'),
codeBlock(diagnosticExample),
heading('Три остановки, которые защищают от ложного вывода'),
paragraph('Первая остановка — error route. В fixture неизвестное property, target или from возвращают <code>invalid-update-contract</code>, stages пусты и DOM не меняется. Это означает: модель не знает, что исследует. В проекте аналогом будет handler, который не является владельцем нужного result, или тест, который не воспроизводит действие. Нельзя назвать такой маршрут «быстрым»: он просто не прошёл входную проверку.'),
paragraph('Вторая остановка — no-op. Если old value равен new value, model возвращает <code>same-className-no-frame-planned</code>. Реальный браузер может иметь другую сопутствующую работу, но конкретный contract не создал нового visual result. Поэтому recording надо пересобрать: возможно, действие уже было выполнено, state пришёл из cache или выбран неверный элемент. Продолжать искать layout в таком case — значит исследовать не то, что пользователь сделал.'),
paragraph('Третья остановка — teaching boundary. Она не говорит «нашли jank», а запрещает довольствоваться суммой. Когда 11 больше 10, требуется одно ограниченное действие и повтор. Сумма не выбирает действие сама. Владелец может быть handler, selector, geometry read или код, который ставит className. Без связи с реальной записью она остаётся условной веткой fixture. Это ограничение полезно тем, что не позволяет превратить диаграмму в результат расследования.'),
heading('Обратимое действие: одна гипотеза, один owner'),
paragraph('Хорошая первая правка маленькая и отменяемая. Например, временно вынести один geometry read из handler, изменить локальное правило или отключить один visual effect за feature toggle. Нельзя заранее обещать, что это уменьшит layout или paint: сначала надо посмотреть новую запись. У правки есть owner, expected DOM after и путь rollback. Если change не сохраняет result, она не проходит функциональный gate. Если после change связь с записью неясна, evidence недостаточен для сильного вывода.'),
paragraph('Fixture проверяет обратимость явно. Forward переводит карточку в <code>is-ready</code>; rollback — обратно в <code>is-pending</code>. У rollback отдельный frame id и тот же учебный порядок стадий. Итоговый DOM должен совпасть с base DOM. Этот test не говорит, что браузер отменяет frame или что в реальном интерфейсе нет побочных эффектов. Он удерживает более скромный контракт: автор умеет вернуть наблюдаемое состояние и не выдаёт «удалить изменение» за оптимизацию.'),
codeBlock(rollbackExample),
heading('Маршрут: симптом → причина → проверка → действие'),
orderedList([
'<strong>Симптом.</strong> В конкретном сценарии интерфейс отвечает неровно, но trace содержит слишком много событий.',
'<strong>Причина.</strong> Нет связи между user action, DOM result и выбранным range; слова style/layout/paint стали общими ярлыками.',
'<strong>Проверка result.</strong> Зафиксируйте target, property, before и after. Если after не тот, расследование заканчивается до Performance panel.',
'<strong>Проверка range.</strong> Повторите один action, выделите одну interaction и сопоставьте её с handler. Отделите этот range от соседних callbacks.',
'<strong>Проверка гипотезы.</strong> Назовите один owner и один вопрос, который можно опровергнуть новой записью. Не переносите synthetic labels на реальные event names.',
'<strong>Действие и rollback.</strong> Внесите маленькую обратимую правку, повторите сценарий и проверьте и DOM after, и новый range.',
]),
heading('Что положить в результат исследования'),
paragraph('Хороший результат помещается в несколько строк: версия браузера и условия записи; шаг действия; DOM before/after; ссылка на handler; выбранный range; один наблюдаемый факт; одна гипотеза; commit или toggle для rollback. Здесь нет места фразе «ускорили рендер», пока нет измерения, метода и сравниваемого варианта. Даже если реальная запись стала лучше, сначала нужно назвать, что именно изменилось и при каких условиях. Это язык системного практика 2022 года: достаточно строгий, чтобы другой инженер продолжил проверку, и достаточно скромный, чтобы не придумать эффект.'),
paragraph('Исторические источники помогают обозначить границы. WHATWG snapshot от 27 февраля 2022 года показывает, что rendering обновляет user agent, а не приложение по фиксированному авторскому списку. W3C Working Group Note объясняет назначение animation frame callback, но fixture не вызывает <code>requestAnimationFrame</code>. Performance Timeline draft описывает работу с performance entries, но учебный result не является entry или trace. На этом материале нельзя обосновать актуальный интерфейс DevTools или поздние performance guidance; для настоящей записи всегда проверяйте версию конкретного браузера.'),
heading('Следующий проверяемый шаг'),
paragraph('Выберите один button на своей странице и проведите маршрут один раз без оптимизации. Итогом будут не числа, а четыре артефакта: DOM before/after, ссылка на mutation, один range и одна отменяемая гипотеза. Запустите fixture пакета, чтобы увидеть, что no-op, error и rollback не маскируются под успешный кадр. Затем проведите реальную запись по тому же контракту. Если она не подтверждает связь, это полезный результат: нужно менять сценарий или вопрос, а не публиковать удобную причину.'),
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.