Рендеринг: как изменение становится пикселями
Что такое ре-рендер на самом деле, почему интерфейс — это функция от состояния, почему ключи списка решают, какую строку браузер переиспользует, и почему измерять лучше, чем угадывать.
Счётчик, который работает, выглядит как магия примерно один день. Потом вы добавляете на страницу список, строки начинают вести себя странно, и магия превращается в вопрос: что именно происходит между щелчком и новыми пикселями?
Сначала разберёмся со словом. Перезагрузка означает, что браузер выбрасывает страницу и снова просит у сервера всё: новый документ, новые элементы, а всё состояние, которое у вас было, пропадает. Ре-рендер совсем не такой. Страница остаётся, вкладка остаётся, а функции компонентов выполняются заново и выдают свежее описание интерфейса. К серверу никто не обращается, если ваш код не попросит данные.
Поэтому интерфейс и записывают как функцию от состояния. Вы не описываете переход от старого экрана к новому. Вы описываете, каким экран должен быть при текущих данных, а разницу вычисляет фреймворк.
function Total({ items }) {
const sum = items.reduce((acc, item) => acc + item.price, 0);
return <p>{sum}</p>;
}
const view = <Total items={cart} />;При одной и той же корзине эта функция всегда возвращает одну и ту же сумму.Теперь классическая ошибка. Вы рисуете список из данных, и каждая строка получает устойчивый key, который определяет сам элемент, а не его позицию.
function Rows({ items }) {
return (
<ul>
{items.map((item, index) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
items.map((item) => <li>{item.name}</li>);Первая версия даёт фреймворку устойчивую личность для каждой строки. Вторая не даёт ничего.Key — это то, по чему фреймворк понимает, что вот эта строка на экране и вон тот элемент в новых данных — одно и то же. Без ключей он опирается на позицию: первая строка это первая строка, вторая это вторая. Добавьте элемент в начало списка текстовых полей, и значения как будто сдвинутся на строку вниз, потому что браузер сохранил первое поле и всего лишь поменял подпись над ним. Данные не были неправильными. Строки просто не сопоставились с нужными элементами.
Если ре-рендер компонента — это повторный запуск функции, почему это не ужасно медленно? Потому что обычно нет. Одно нажатие клавиши может запустить несколько маленьких функций, которые построят несколько сотен обычных объектов, а это намного дешевле, чем отрисовка кадра браузером. Это доступно по цене так, как двадцать лет назад было просто недоступно, и современные приложения стоят именно на этом факте.
Именно поэтому честный совет про скорость скучный: измеряйте, прежде чем оптимизировать. Страница может перерисовываться тысячи раз в секунду и ощущаться мгновенной, а одна картинка не того размера заставит её ощущаться сломанной. Угадывать, какая часть медленная, — самый ненадёжный инструмент из доступных; в браузере уже есть профилировщики, которые просто скажут вам это.
Практика
Счётчик мгновенно обновляет число, адрес в строке браузера не меняется, а остальная страница сохраняет позицию прокрутки. Что произошло?
Подсказки
- Следите за адресной строкой и позицией прокрутки: перезагрузка их уничтожает и создаёт заново, а ре-рендер нет.
- Ре-рендер снова проходит по тем же функциям компонентов. Он не просит у сервера страницу заново.
Поле поиска один раз запрашивает результаты у API, а затем фильтрует их на месте при каждом нажатии клавиши. Расставьте шаги по порядку.
- 1Компонент вычисляет, какие результаты подходят под текущий текст
- 2Он возвращает элементы для подходящих строк, у каждой свой устойчивый key
- 3Полученный список сохраняется как state, чтобы пережить следующие запуски
- 4Компонент один раз просит у сервера список
- 5Фреймворк обновляет только те строки, содержимое которых изменилось
Подсказки
- Ничего нельзя вычислить, пока данные не получены, и ничего нельзя нарисовать, пока не вычислено.
- Кнопка — это не значение. Щелчок только просит об изменении; само изменение происходит на следующем шаге.
Сопоставьте каждую ситуацию с честным объяснением того, что происходит.
Подсказки
- Один столбец про значение, которое изменилось, другой про то, что с ним делает браузер.
- Спросите, что из этого верно для кнопки счётчика, а что верно для страницы, которая перезагружается.