SkillGridИзучай computer science на практике

Рендеринг: как изменение становится пикселями

Что такое ре-рендер на самом деле, почему интерфейс — это функция от состояния, почему ключи списка решают, какую строку браузер переиспользует, и почему измерять лучше, чем угадывать.

14 мин · 45 XP

Счётчик, который работает, выглядит как магия примерно один день. Потом вы добавляете на страницу список, строки начинают вести себя странно, и магия превращается в вопрос: что именно происходит между щелчком и новыми пикселями?

Сначала разберёмся со словом. Перезагрузка означает, что браузер выбрасывает страницу и снова просит у сервера всё: новый документ, новые элементы, а всё состояние, которое у вас было, пропадает. Ре-рендер совсем не такой. Страница остаётся, вкладка остаётся, а функции компонентов выполняются заново и выдают свежее описание интерфейса. К серверу никто не обращается, если ваш код не попросит данные.

UserAppDOMreloadF5a completely new treere-renderclickonly the text that changedthe same tree, one detail updated

Поэтому интерфейс и записывают как функцию от состояния. Вы не описываете переход от старого экрана к новому. Вы описываете, каким экран должен быть при текущих данных, а разницу вычисляет фреймворк.

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 — это то, по чему фреймворк понимает, что вот эта строка на экране и вон тот элемент в новых данных — одно и то же. Без ключей он опирается на позицию: первая строка это первая строка, вторая это вторая. Добавьте элемент в начало списка текстовых полей, и значения как будто сдвинутся на строку вниз, потому что браузер сохранил первое поле и всего лишь поменял подпись над ним. Данные не были неправильными. Строки просто не сопоставились с нужными элементами.

Если ре-рендер компонента — это повторный запуск функции, почему это не ужасно медленно? Потому что обычно нет. Одно нажатие клавиши может запустить несколько маленьких функций, которые построят несколько сотен обычных объектов, а это намного дешевле, чем отрисовка кадра браузером. Это доступно по цене так, как двадцать лет назад было просто недоступно, и современные приложения стоят именно на этом факте.

Именно поэтому честный совет про скорость скучный: измеряйте, прежде чем оптимизировать. Страница может перерисовываться тысячи раз в секунду и ощущаться мгновенной, а одна картинка не того размера заставит её ощущаться сломанной. Угадывать, какая часть медленная, — самый ненадёжный инструмент из доступных; в браузере уже есть профилировщики, которые просто скажут вам это.

Практика

  1. Счётчик мгновенно обновляет число, адрес в строке браузера не меняется, а остальная страница сохраняет позицию прокрутки. Что произошло?

    Подсказки
    • Следите за адресной строкой и позицией прокрутки: перезагрузка их уничтожает и создаёт заново, а ре-рендер нет.
    • Ре-рендер снова проходит по тем же функциям компонентов. Он не просит у сервера страницу заново.
  2. Поле поиска один раз запрашивает результаты у API, а затем фильтрует их на месте при каждом нажатии клавиши. Расставьте шаги по порядку.

    1. 1Компонент вычисляет, какие результаты подходят под текущий текст
    2. 2Он возвращает элементы для подходящих строк, у каждой свой устойчивый key
    3. 3Полученный список сохраняется как state, чтобы пережить следующие запуски
    4. 4Компонент один раз просит у сервера список
    5. 5Фреймворк обновляет только те строки, содержимое которых изменилось
    Подсказки
    • Ничего нельзя вычислить, пока данные не получены, и ничего нельзя нарисовать, пока не вычислено.
    • Кнопка — это не значение. Щелчок только просит об изменении; само изменение происходит на следующем шаге.
  3. Сопоставьте каждую ситуацию с честным объяснением того, что происходит.

    Подсказки
    • Один столбец про значение, которое изменилось, другой про то, что с ним делает браузер.
    • Спросите, что из этого верно для кнопки счётчика, а что верно для страницы, которая перезагружается.