Как вы могли придумать React
Тот факт, что React навсегда вошел в мир JavaScript, не подлежит сомнению. Из-за этого новички в JavaScript часто не сомневаются в необходимости интерфейсных фреймворков и библиотек, таких как React, и сразу же предполагают, что это необходимо для любого приложения браузера.
Хотя React действительно чрезвычайно полезен, вы можете не сразу понять, почему это так. Итак, давайте совершим небольшое путешествие по гипотетическому приложению, чтобы понять, как вы, возможно, пришли к основам, на которых построен React.
Представим, что у нас есть интерфейс, написанный на HTML и CSS, который в настоящее время имеет только статическое содержимое. Мы создали несколько простых карточек и теперь хотим заполнить их динамическими данными. Раньше мы делали это, обрабатывая нашу разметку на сервере с помощью PHP, echo
обрабатывая столько HTML, сколько необходимо, и в основном это было бы все.
Однако с появлением AJAX одностраничные приложения стали более распространенными. Теперь мы можем получать данные с помощью fetch
API. Это требует перестройки нашего интерфейса в браузере, а не на сервере. У нас больше нет возможности отправлять всю страницу каждый раз, когда появляются новые данные - они поступают к нам асинхронно, и браузер должен их отображать.
Представим, что мы создали прототип следующей разметки как содержимого одной из карточек.
Если бы мы были все еще в 2003 году, мы могли бы просто заменить переменные данные на переменные PHP, обернуть это в теги <?php>
и называть это днем. Однако в этом сценарии мы находимся в 2019 году, но категорически против использования интерфейсных фреймворков.
Я проигнорирую часть этой задачи, касающуюся AJAX, из-за недостатка места, а также потому, что она не очень актуальна для рассматриваемой темы. Мы будем хранить наши данные внутри константы JSON в нашем файле скрипта.
const data = [ { name: 'Anna Komnene', avatar: 'https://i.imgur.com/dE1Xhg4.png', description: '"The truth is all barbarians are usually fickle and by nature unable to keep their pledges."', }, { name: 'John the Grammarian', // ...
Давайте начнем с очень наивного подхода, преобразовав гипотетический пример PHP в JavaScript. Мы могли бы сделать что-нибудь очень глупое:
Это довольно просто. Мы можем использовать шаблонные литералы для рендеринга строк непосредственно в HTML. Его довольно легко реализовать, и он понятен с первого взгляда.
Однако любой опытный разработчик JavaScript будет недоволен этой реализацией. Мы не дезинфицируем пользовательский ввод и не делаем ничего, кроме вывода того, что находится в строках. Если бы наши данные содержали какой-либо пользовательский контент, мы были бы широко открыты для атак с использованием межсайтовых сценариев (XSS). Очистить эти данные не составит особого труда, но если вы попытаетесь это сделать в более крупном масштабе, вы быстро поймете, что они плохо масштабируются.
Вдобавок к этому innerHTML
по сути каждый раз воссоздает дерево DOM с нуля. Это означает, что если бы у нас были какие-либо существующие прослушиватели событий, подключенные к этим узлам, они были бы перезаписаны. Мы можем добиться большего.
Это гораздо более чистый подход, хотя он все же требует довольно много повторений. Использование textContent
гарантирует, что любой случайный код XSS, который может попасть в наши данные, не будет выполнен. Кроме того, создание модели DOM вручную намного эффективнее и понятнее, чем при использовании innerHTML
.
Однако вы заметите, что и здесь много повторений. Для каждого из этих элементов мы повторяем один и тот же код: каждый использует createElement
, а каждый использует appendChild
. Мы можем создать функцию, чтобы упростить это.
Это немного помогло: мы создали функцию с именем createElement
, которая принимает тип элемента (в основном HTML-тег), ссылку на желаемый родительский элемент элемента и функцию, которая позволяет нам работать с этим элементом дальше.
Теоретически, поскольку мы используем функцию для работы с вновь созданным элементом, мы также могли бы сделать что-то вроде этого:
Однако вы заметите, что на самом деле здесь мы делаем только две вещи: назначаем атрибуты и изменяем текстовое содержимое. Так что мы могли бы обобщить это еще дальше.
Наш код становится более сложным. Наша функция createElement
получила две новые части: сначала мы перебираем переданный ей объект attributes
и добавляем их в наш элемент DOM. Затем, если есть контент, который нужно добавить, мы добавляем его.
Однако при таком подходе вы заметите, что мы потеряли интересное свойство связывания createElement
функций вместе. Это было бы желательной функцией, потому что, поскольку функция теперь более надежна, мы могли бы в идеале создать приближение дерева DOM, вложив эти функции.
Давайте изменим функцию createElement
, чтобы это поддержать.
Это изменило интерфейс функции createElement
, однако теперь ее намного проще вызывать, даже если она немного увеличилась в размере. Давайте подробнее рассмотрим, что на самом деле делает эта функция.
Сначала мы создаем узел DOM, используя метод createElement
. Все просто.
Затем мы проверяем, есть ли у нас какие-либо атрибуты для обработки. Если мы это сделаем, мы перебираем ключи этих атрибутов и устанавливаем их для нашего вновь созданного элемента.
Затем мы проверяем, передан ли массив дочерних элементов. Если есть, мы перебираем его. Дочерние элементы могут быть двух типов: строки или объекты. Если это строки, мы создаем текстовый узел и добавляем его к элементу (мы не можем просто назначить textContent
здесь, потому что могут быть другие дочерние элементы, и такое назначение перезапишет их).
Если дочерний элемент является объектом, мы просто вызываем createElement
рекурсивно, применяя свойства этого дочернего элемента к функции, за исключением parent
, поскольку это, очевидно, дочерний элемент текущего обрабатываемого элемента.
Это меняет нашу реализацию на это:
И это примерно то, что представляет собой функциональный компонент React. За причудливым синтаксисом JSX, за его умным именованием, за действительно красивой реализацией виртуальной модели DOM и за многими, многими оптимизациями - вот и все. По крайней мере, концептуально.
Проще говоря, это всего лишь функция, которая принимает свойства в качестве аргумента и возвращает компонент.
Почти как innerHTML
, согласны?
📝 Прочтите этот рассказ позже в Журнале.
👩💻 Просыпайтесь каждое воскресное утро и слушайте самые интересные истории из области технологий, ожидающие вас в вашем почтовом ящике. Прочтите информационный бюллетень« Примечательно в технологиях .