Потому что почему бы и нет?!

Хотя это не веб-стандарт, и веб-компоненты пытаются заменить его, JSX — это удивительная технология, которая появилась вместе с React, чтобы упростить способ совместного написания HTML и JavaScript.

Но как именно это работает? Я имею в виду, да, мы можем вернуть JSX из компонента React, но мы все знаем, что это не стандартный JavaScript, так как же он работает? Какое волшебство стоит за этим?

Я лично люблю, когда технология «просто работает», но если от этого зависит моя работа, я стараюсь понять ее как можно лучше. И один из способов сделать это — попытаться перепроектировать, как это работает, и написать свою собственную версию.

В этом процессе вы многому научитесь!

Итак, в этой статье я собираюсь показать вам, как вы можете написать собственную версию парсера JSX, который может превратить «компонент» JSX в компонент JavaScript, который фактически возвращает корректный HTML.

Пойдем!

JSX, который мы будем анализировать

Давайте начнем с конца, давайте посмотрим на файл JSX, который мы будем анализировать.

Если бы вы написали это в React, у вас было бы что-то вроде этого:

Честно говоря, единственная изменяющаяся часть — это первоначальный импорт, и мы на самом деле увидим, ПОЧЕМУ нам нужно импортировать React, когда мы хотим написать JSX.

Хотя сам синтаксический анализ требует некоторой работы, логика, стоящая за ним, на самом деле довольно проста. Если вы посмотрите документацию Rect, они покажут вам результаты синтаксического анализа JSX.

Обратите внимание, как по существу вы превращаете каждый элемент JSX в вызов React.createElement . Да! Вот почему вам нужно импортировать React, даже если вы не используете его напрямую, после анализа результирующий JavaScript будет использовать его.

Теперь, когда мы разгадали эту загадку, давайте продолжим.

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

Вот и все, задача здесь состоит в том, чтобы:

  1. Захватите JSX внутри JavaScript.
  2. Разберите его в древовидную структуру, которую мы можем просматривать и запрашивать.
  3. Переведите эту структуру в код JavaScript (текст), который будет написан вместо JSX.
  4. Сохраните результат шага 3 на диск в файл с расширением .js.

Приступаем к кодированию!

Извлечение и разбор JSX из компонента

Первый шаг включает в себя извлечение JSX из компонента и преобразование его в древовидную структуру.

Конечно, это два шага, но мы сделаем их вместе.

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

Получив его, мы можем использовать анализатор HTML, чтобы понять его.

Имейте в виду, что мы можем сделать это здесь, потому что на данный момент все, что нам нужно, это структура, а не фактические функции JSX. Поэтому мы будем использовать модуль fs из Node для чтения файла и пакет node-html-parser.

Функция выглядит следующим образом:

Эта функция использует RegExp для поиска открывающего тега первого компонента внутри раздела (...) функции. В строке 10 мы вызываем функцию parse, которая возвращает элемент root, где firstChild — это корневой элемент из нашего JSX (в нашем случае это обертка div).

Теперь, когда у нас есть древовидная структура, давайте начнем переводить ее в код. Для этого мы вызовем функцию translate.

Преобразование кода HTML в JS

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

Вот как выглядит функция, разберем ее ниже:

Сначала мы пройдемся по всем дочерним элементам и вызовем для них функцию translate. Если дочерние элементы пусты, то этот вызов вернет null, и мы отфильтруем эти результаты в строке 7.

После того, как мы разобрались с дочерними элементами, давайте взглянем на строку 9, где мы делаем быструю проверку работоспособности для типа узла. Если тип равен 3, это означает, что это Text Node, что является причудливым словом для «просто текста», поэтому мы вернем проанализированный текст.

Почему мы вызываем функцию parseText? Потому что даже внутри текстовых узлов нам нужно искать выражения JSX внутри {…} . Таким образом, эта функция позаботится о проверке и правильном изменении возвращаемой строки, если это необходимо.

После этого, что означает, что мы на самом деле имеем дело не с текстовым узлом, мы получим имя тега (строка 14) и проанализируем атрибуты (строка 16). Анализ атрибутов означает, что мы возьмем эту необработанную строку и превратим ее в правильный JSON.

Наконец, мы вернем строку кода, которую хотим сгенерировать (т. е. вызов createElement с правильными параметрами). Это то, что происходит в строке 18.

Помните, что мы пишем код, а не запускаем его. Вот почему все внутри строк.

Наконец, последняя деталь, которую следует отметить в этой функции, заключается в том, что сгенерированный код вызывает метод createElement из модуля MyLib. Вот почему у нас есть import * as MyLib from './MyLib.js’ внутри файла JSX.

Теперь нам нужно начать работать со строками, чтобы заменить выражения JSX, как внутри текстовых узлов, так и в объекте свойств каждого элемента.

Понравилось ли вам то, что вы прочитали? Подпишитесь на мой БЕСПЛАТНЫЙ информационный бюллетень, где я делюсь со всеми своим 20-летним опытом работы в ИТ-индустрии. Присоединяйтесь к Бродяге старого разработчика!

Разбор выражений

Тип выражения JSX, который я поддерживаю в этой реализации, является самым простым. Как вы видите в примере, вы можете добавить переменные JS внутрь этих выражений, и они останутся переменными в конечном выводе.

Вот функции, которые это делают:

Если у нас есть интерполяции (т. е. переменные внутри фигурных скобок), то мы вызываем функцию replaceInterpolation, которая проходит через все соответствующие интерполяции и заменяет их правильно отформатированной строкой (по сути, оставляя имя переменной таким образом, что при записи будет генерироваться переменная JS). в JS-файл).

Мы используем эти функции также с объектом атрибутов. Поскольку мы используем метод JSON.stringify при возврате кода JS, эта функция преобразует все значения в строки (ну, почти все, но определенно те, которые мы определили как отдельные переменные). Поэтому вместо этого мы проанализируем строку, возвращаемую методом stringify, и обязательно заменим интерполированные переменные правильно.

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

Давайте теперь посмотрим на код вывода при разборе нашего JSX-файла.

Код JavaScript

Результат чтения и анализа моего глупого кода JSX следующий:

Что действительно интересно в этом коде, так это сгенерированные вызовы createElement . Вы можете видеть, как они вложены и ссылаются на переменные, которые я интерполировал в файле JSX.

Если мы выполним этот код, вывод будет:

Но последний вопрос остается без ответа: как реализован метод createElement? Ну, у меня тоже есть упрощенная версия:

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

И все, магия раскрыта!

JSX — одна из моих любимых технологий, и она определенно упрощает работу и создание HTML из файлов JS.

Конечно, вы всегда можете написать свой код, используя метод createElement напрямую, но это будет непросто и не очень хорошо для сложных приложений.

Помните, что вы можете посмотреть полный исходный код этого проекта здесь, а если у вас есть какие-либо вопросы по этому поводу, оставьте комментарий здесь, и мы можем поговорить об этом!

Создавайте приложения с повторно используемыми компонентами, такими как Lego.

Инструмент с открытым исходным кодом Bit помогает более чем 250 000 разработчиков создавать приложения с компонентами.

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

Подробнее

Разделите приложения на компоненты, чтобы упростить разработку приложений, и наслаждайтесь наилучшими возможностями для рабочих процессов, которые вы хотите:

Микро-интерфейсы

Система дизайна

Совместное использование кода и повторное использование

Монорепо

Узнать больше