Я лично большой поклонник jsx & love, как позволяет мне разделить и компонент мой код. Несмотря на то, что JSX было вокруг, прежде чем отреагировать, это не было бы почти так же популярным, без реагирования на снятие. Однако мы можем на самом деле использовать JSX без реагирования, а также не так сложно.
Путь React Works – настроить ваш Bundler для преобразования JSX в вызовы функции CreateeEdeeEdeEdement. Так, например:
const foo = () // Would become this: React.createElement( 'div', { className: 'cool' }, React.createElement('p', null, 'Hello there!') )Hello there!
Однако большинство трансполков позволяют вам выбрать свой собственный jsx Pragma (функция, которая будет в месту ract.createidelement
). Например, если вы использовали Babel, вы можете указать, какую функцию использовать через простой комментарий, как это:
/** @jsx myJsxFunction */ const foo = ()Hello there!
И теперь Бабел пройдет те некоторые параметры для myjsxfunction
Отказ Теперь все, что нам нужно сделать, это создать функцию, которая принимает эти параметры и созданные реальные узлы DOM, которые мы можем добавить в наш дом. Так что давайте начнем. (Если вам нужен код Sandbox для воспроизведения, вы можете использовать этот статический шаблон с использованием автономного бабела )
Узлы DOM созданы с помощью Document.CreateNode ()
Функция, и это требует всего лишь тарнама, поэтому хорошее место для начала будет с этим:
export const createElement = (tag, props, ...children) => { const element = document.createElement(tag) return element }
Теперь, когда у нас есть узел DOM, мы должны на самом деле добавлять приданные нам атрибуты. Это может быть что-нибудь вроде сорт
или стиль
. Итак, мы просто будем пить через все предоставленные атрибуты (используя object.entries
И просто установите их на нашем узле DOM:
export const createElement = (tag, props, ...children) => { const element = document.createElement(tag) Object.entries(props || {}).forEach(([name, value]) => { element.setAttribute(name, value.toString()) }) return element }
У этого подхода есть одна проблема. Как мы обращаемся к событиям, например, если у меня есть этот JSX:
const SayHello = ()
Наша функция установила бы OnClick в качестве обычного атрибута с обратным вызовом в качестве фактического текста. Вместо этого мы можем сделать, это проверьте, начинается ли наш атрибут с «ON» и находится в окне. Это скажет нам, если это событие или нет. Например onclick
Это в окне охвата, однако onfoo
не является. Если это тогда, мы можем зарегистрировать слушатель события на этом узле, используя часть имени без «ON».
Вот как это выглядит:
export const createElement = (tag, props, ...children) => { const element = document.createElement(tag) Object.entries(props || {}).forEach(([name, value]) => { if (name.startsWith('on') && name.toLowerCase() in window) element.addEventListener(name.toLowerCase().substr(2), value) else element.setAttribute(name, value.toString()) }) return element }
Хороший! Теперь все, что осталось сделать, это добавить всех детей к родителю. Однако вы не можете добавить строку в узел DOM, так что в случае, если ребенок не является также узлом, мы можем создать текстовый узел и добавить, что вместо этого:
export const createElement = (tag, props, ...children) => { const element = document.createElement(tag) Object.entries(props || {}).forEach(([name, value]) => { if (name.startsWith('on') && name.toLowerCase() in window) element.addEventListener(name.toLowerCase().substr(2), value) else element.setAttribute(name, value.toString()) }) children.forEach(child => { element.appendChild( child.nodeType === undefined ? document.createTextNode(child.toString()) : child ) }) return element }
Однако это быстро работает с проблемами с глубоко вложенными элементами, а также элементами, которые создаются с использованием карт массива. Поэтому вместо этого давайте заменим эту часть с аа рекурсивный Приложение
Метод:
const appendChild = (parent, child) => { if (Array.isArray(child)) child.forEach(nestedChild => appendChild(parent, nestedChild)); else parent.appendChild(child.nodeType ? child : document.createTextNode(child)); };
И теперь мы можем использовать это вместо нашего старого метода:
export const createElement = (tag, props, ...children) => { const element = document.createElement(tag) Object.entries(props || {}).forEach(([name, value]) => { if (name.startsWith('on') && name.toLowerCase() in window) element.addEventListener(name.toLowerCase().substr(2), value) else element.setAttribute(name, value.toString()) }) children.forEach(child => { appendChild(element, child); }); return element }
Оно работает! Попробуй это. Теперь мы можем сделать базовый JSX к DOM:
import { createElement } from "./Vanilla" /** @jsx createElement */ const App = () document.getElementById("root").appendChild(App)My awesome app :)
И вы должны увидеть, что ваш JSX оказывается прекрасно. Есть еще несколько вещей, которые мы можем добавить, хотя, например, в реакции, элементы обычно функционируют, реализующие это позволит нам гнездить компоненты и воспользоваться преимуществами реквизита, которые имеют решающее значение.
К счастью, это довольно просто для реализации. Все, что нам нужно сделать, это проверьте, является ли Tagname функцией вместо строки. Если мы не делаем никаких других вещей, а скорее просто позвоните в функцию. Вот как это выглядит:
export const createElement = (tag, props, ...children) => { if (typeof tag === "function") return tag(props, children) {...} }
И теперь давайте попробуем это:
import { createElement } from "./Vanilla" /** @jsx createElement */ const SayHello = props => () /*Hello {props ? props.name : "world"}
I hope you're having a good day
=== Component() */ document.getElementById("root").appendChild( )
Как вы можете увидеть реализацию, который позволил нам также использовать реквизиты! Вы можете сказать, что мы сделали здесь Но есть еще одна особенность, которую я хочу реализовать, и это фрагменты. Для тех, кто не знакомы фрагменты – это способ иметь пустые контейнеры в JSX, и они используют пустые теги. Пример:
/** @jsx createElement */ /** @jsxFrag createFragment */ const UsingFragment = () => ()This is regular paragraph
<>This is a paragraph in a fragment
Но для этого на работу нам нужна функция, которая принимает этот фрагмент и вместо создания элемента DOM для него просто возвращает его дети. Вот как это выглядит:
const createFragment = (props, ...children) => { return children; }
Вот и все! Это работает из коробки из-за нашего рекурсивного Приложение
метод.
Вот и все! Мы сделали это. Супер простые JSX к DOM-функции, которые нам используют мощность JSX без необходимости реагирования. Вы можете найти исходный код для этого Это кодовая коробка Отказ
Я надеюсь, что вы нашли этот пост полезным, и я также надеюсь, что вы найдете несколько классных способов использования мощности JSX. Я на самом деле узнал обо всем этом, работая над ДОВ , который является генератором статического сайта JSX для Node.js. Он в основном позволяет вам написать код стиля Next.js, но преобразует его в статический HTML без увлажнения. Проверьте это и дайте мне знать, что вы думаете. Мир!
Оригинал: “https://dev.to/kartiknair/using-jsx-without-react-28eb”