Рендеринг и фиксация в React

10 месяцев назад·1 мин. на чтение

Прежде чем ваши компоненты отобразятся на экране, они должны быть обработаны React. Понимание шагов этого процесса поможет вам понять, как выполняется ваш код, и объяснить его поведение.

Содержание туториала по React Прежде чем ваши компоненты отобразятся на экране, они должны быть обработаны React. Понимание шагов этого процесса поможет вам понять, как выполняется ваш код, и объяснить его поведение. Представьте, что ваши компоненты — повара на кухне, собирающие вкусные блюда из ингредиентов. В этом сценарии React — это официант, который принимает запросы от клиентов и приносит им их заказы. Этот процесс запроса и обслуживания пользовательского интерфейса состоит из трех этапов:
  1. Запуск рендера (доставка заказа гостя на кухню)
  2. Рендер компонента (подготовка заказа на кухне)
  3. Фиксация (commit) в DOM (размещение заказа на столе)

Шаг 1. Запуск рендеринга

Есть две причины для рендеринга компонента:
  • Это начальный рендер компонента.
  • Состояние компонента (или одного из его предков) было обновлено.

Начальный рендер

Когда ваше приложение запускается, вам нужно запустить первоначальный рендеринг. Фреймворки и песочницы иногда скрывают этот код, но это делается путем вызова createRoot с целевым узлом DOM, а затем вызова его метода рендеринга с вашим компонентом:
// index.js

import Image from './Image.js';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'));
root.render(<Image />);
// Image.js

export default function Image() {
  return <img src="https://example.com/image.jpg" alt="image" />;
}

Рендеринг при обновлении состояния

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

Шаг 2: React визуализирует ваши компоненты

После запуска рендеринга React вызывает ваши компоненты, чтобы выяснить, что отображать на экране. «Рендеринг» — это React, вызывающий ваши компоненты.
  • При первоначальном рендеринге React вызовет корневой компонент.
  • Для последующих рендеров React будет вызывать функциональный компонент, обновление состояния которого инициировало рендеринг.
Этот процесс является рекурсивным: если обновленный компонент возвращает какой-то другой компонент, React отрисовывает этот компонент следующим, а если этот компонент также что-то возвращает, он отрисовывает этот компонент следующим и так далее. Процесс будет продолжаться до тех пор, пока не останется вложенных компонентов и React точно не будет знать, что должно отображаться на экране. В следующем примере React вызовет Gallery() и Image() несколько раз:
// Gallery.js

export default function Gallery() {
  return (
    <section>
      <h1>Inspiring Sculptures</h1>
      <Image />
      <Image />
      <Image />
    </section>
  );
}

function Image() {
  return <img src="https://example.com/image.jpg" alt="image" />;
}
// index.js

import Gallery from './Gallery.js';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'));
root.render(<Gallery />);
  • Во время первоначального рендеринга React создаст узлы DOM для тегов <section>, <h1> и трех тегов <img>.
  • Во время повторного рендеринга React вычислит, какие из их пропсов, если таковые имеются, изменились с момента предыдущего рендеринга. Он ничего не будет делать с этой информацией до следующего шага, фазы фиксации.
Рендеринг всегда должен быть чистым:
  • При тех же входах, тот же выход. При одинаковых входных данных компонент всегда должен возвращать один и тот же JSX. (Когда кто-то заказывает салат с помидорами, он не должен получать салат с луком.)
  • Он думает только о своих делах. Он не должен изменять какие-либо объекты или переменные, существовавшие до рендеринга. (Один заказ не должен изменять чей-либо другой заказ.)
В противном случае вы можете столкнуться с запутанными ошибками и непредсказуемым поведением по мере усложнения вашей кодовой базы. При разработке в «строгом режиме» React дважды вызывает функцию каждого компонента, что может помочь обнаружить ошибки, вызванные нечистыми функциями.

Оптимизация производительности

Поведение рендеринга по умолчанию всех компонентов, вложенных в обновленный компонент, не является оптимальным для производительности, если обновленный компонент находится очень высоко в дереве. Если вы столкнулись с проблемой производительности, существует несколько способов ее решения, описанных в разделе «Производительность». Не оптимизируйте преждевременно.

Шаг 3: React фиксирует изменения в DOM

После рендеринга (вызова) ваших компонентов React изменит DOM.
  • Для начального рендеринга React будет использовать DOM API appendChild() для размещения на экране всех созданных DOM-узлов.
  • Для повторного рендеринга React применит минимально необходимые операции (рассчитанные во время рендеринга), чтобы привести DOM в соответствие с последним результатом рендеринга.
React изменяет DOM узлы только в том случае, если между рендерами есть разница. Например, вот компонент, который каждую секунду перерисовывается с разными пропсами, переданными от его родителя. Обратите внимание, как вы можете добавить некоторый текст в <input>, обновив его значение, но текст не исчезнет при повторном рендеринге компонента:
// Clock.js

export default function Clock({ time }) {
  return (
    <>
      <h1>{time}</h1>
      <input />
    </>
  );
}
Это работает, потому что на этом последнем шаге React только обновляет содержимое <h1> новым значением времени. Он видит, что <input> появляется в JSX в том же месте, что и в прошлый раз, поэтому React не касается <input> или его значения.

Отрисовка браузером

После того, как рендеринг завершен и React обновил DOM, браузер перерисует экран. Хотя этот процесс известен как "рендеринг в браузере", мы будем называть его "отрисовкой", чтобы избежать путаницы в остальной части этого руководства.

Резюме

Любое обновление экрана в приложении React происходит в три этапа:
  • Запуск
  • Рендеринг
  • Фиксация
Вы можете использовать строгий режим для поиска ошибок в ваших компонентах. React не затрагивает DOM, если результат рендеринга такой же, как и в прошлый раз.

12 популярных вопросов с ответами на JavaScript собеседовании

год назад·4 мин. на чтение

Эта статья предназначена для junior разработчиков, которые хотят получить свою первую работу в качестве JavaScript разработчиков.

1. Что такое JavaScript и для чего он используется?

Javascript - это высокоуровневый, динамичный и интерпретируемый язык программирования, широко используемый для многих сред, таких как интерфейс веб-сайтов, backend службы, desktop и мобильные приложения. Но чаще всего JavaScript используется для создания динамических и интерактивных веб-сайтов.

2. Что такое замыкание в JavaScript?

Замыкание — это функция, которая имеет доступ к переменным своей внешней функции. Это важно, потому что это позволяет внутренней функции «запоминать» переменные из своей внешней области, поэтому она может продолжать получать к ним доступ и манипулировать ими даже после того, как внешняя функция была завершена.
function outerFunction(x) {
  return function innerFunction(y) {
    return x + y;
  };
}

const add5 = outerFunction(5);
console.log(add5(3)); // 8

3. Как this работает в JavaScript?

this важное ключевое слово в JavaScript. Его значение определяется тем, как вызывается функция. Его можно задать явно с помощью call(), apply() или bind(). По умолчанию его значение внутри функции устанавливается в глобальный объект (в браузере это объект window), за исключением того, когда функция вызывается как метод объекта. В этом случае this указывает на объект, методом которого он является.

4. Что такое поднятие (hoisting) в JavaScript?

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

5. В чем разница между var, let и const?

  • var: Переменные, объявленные с помощью var, имеют функциональную область действия, что означает, что к ним можно получить доступ только в рамках функции, в которой они были объявлены. Внешняя переменная затеняется, когда переменная с тем же именем объявляется во вложенной функции.
  • let: Пусть переменные имеют блочную область действия, что означает, что они доступны только в том блоке, в котором они объявлены. Объявление переменной с тем же именем во вложенном блоке приведет к затенению внешней переменной.
  • const: переменные, объявленные с помощью const, также как let, имеют область действия блока, но их нельзя переназначить.
Вот пример:
function example() {
  var x = 1;
  let y = 2;
  const z = 3;

  if (true) {
    var x = 4;
    let y = 5;
    const z = 6;
  }

  console.log(x); // 4
  console.log(y); // 2
  console.log(z); // 3
}

example();

6. Какова разница между == и ===?

Оператор равенства == выполняет принудительное приведение типа, что означает, что он попытается преобразовать операнды в один и тот же тип перед их сравнением. С другой стороны, оператор строгого равенства ===, не выполняет приведение типа. Он возвращает значение true только в том случае, если оба операнда имеют одинаковый тип и значение. Например:
console.log(1 == '1'); // true
console.log(1 === '1'); // false

7. В чем разница между null и undefined?

undefined указывает, что переменная была объявлена, но ей не было присвоено значение. Значение nullпредставляет собой преднамеренное отсутствие какого-либо значения объекта. Другими словами, null — это явно заданное значение, указывающее на отсутствие значения.

8. Что такое событие в JavaScript?

В JavaScript событие — это любое действие в браузере, например нажатие пользователем кнопки, загрузка страницы или обновленный элемент. Слушатели событий обычно обрабатывают события, позволяя разработчикам указывать функции, которые должны выполняться при возникновении события. Пример:
const button = document.querySelector('button');

button.addEventListener('click', () => {
  console.log('Button was clicked!');
});

9. В чем разница между синхронным и асинхронным кодом в JavaScript?

Синхронный код выполняется блокирующим образом, что означает, что следующая строка кода будет выполнена после завершения текущей строки. С другой стороны, асинхронный код выполняется неблокирующим образом, что означает, что другой код может выполняться не ожидая завершения асинхронного кодом. Асинхронный код обычно реализуется в JavaScript с помощью обратных вызовов или Promises.

10. Что такое промисы (Promise) в JavaScript?

Объект Promise — это объект, представляющий успешное или неудачное завершение асинхронной операции. Promise позволяет регистрировать обратные вызовы для получения уведомлений о завершении или сбое асинхронной операции, а также обрабатывать ошибки более удобно и централизованно. Например:
const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Data fetched!');
    }, 1000);
  });
};

fetchData()
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error(error);
  });

11. Что такое обратный вызов в JavaScript?

Функция обратного вызова передается в качестве аргумента другой функции и выполняется после завершения внешней функции. Разработчики могут использовать обратные вызовы для указания кода, который будет выполняться после завершения асинхронной операции. Пример:
const fetchData = (callback) => {
  setTimeout(() => {
    callback('Data fetched!');
  }, 1000);
};

fetchData(data => {
  console.log(data);
});

12. Что такое AJAX в JavaScript?

AJAX (асинхронный JavaScript и XML) — это метод выполнения асинхронных серверных запросов с веб-страницы без перезагрузки всей страницы. AJAX позволяет разработчикам динамически обновлять веб-страницу новыми данными без необходимости обновления страницы. Это достигается путем отправки HTTP-запроса из браузера на сервер и обновления только тех частей страницы, которые требуют его с ответом. Это улучшает взаимодействие с пользователем, поскольку страница не перезагружается, а обновляются только необходимые данные. Пример:
const xhr = new XMLHttpRequest();

xhr.open('GET', 'https://api.example.com/data');

xhr.onreadystatechange = () => {
  if (xhr.readyState === XMLHttpRequest.DONE) {
    console.log(xhr.responseText);
  }
};

xhr.send();
Еще больше вопросов с собеседований можно найти здесь.