Запуск проекта был легкой частью, так как каждый проект узла yarn init -y
будет создавать базовые объекты, и это почти еженедельная задача для энтузиастов JS, поэтому давайте не будем углубляться в эту команду; теперь зависимости очень минимальны, лично мне нравится использовать webpack, но для простоты давайте просто воспользуемся babel:
yarn add -D @babel/cli @babel/core @babel/plugin-syntax-jsx @babel-plugin-transform-react
Следующий шаг также тривиален, определите .babelrc
файл в корне и используйте это содержимое:
{ “plugins”: [ “@babel/plugin-syntax-jsx”, [“@babel/plugin-transform-react-jsx”, { “pragma”: “dom” }] ] }
Здесь необходимо небольшое пояснение; с помощью этих шагов мы можем преобразовать <h1>Hi</h1>
в dom(“h1”, null, “Hi”)
двумя способами,
- Определение прагмы в .babelrc - это то, что будет называть fn, иначе по умолчанию будет
React.createElement
- Выполняем одну
@babel/cli
команду, но давайте воспользуемся сценариями npm изpackage.json
, чтобы она была полезной:
"scripts": { "build": "babel example.js --out-dir lib" }
Теперь, если вы читаете это, скорее всего, вы уже знаете, что такое JSX (на основе XML). Из-за этого у меня возникли некоторые проблемы, когда меня попросили снова реализовать компонент, который ранее был написан на React, но на простом JS. Сказанная причина заключалась в том, что компонент был настолько прост, что стало излишним пытаться заставить каждую команду в организации использовать его, поскольку им придется включать множество зависимостей для проектов, которые мало или не имеют ничего общего с React, но не то, чтобы это было не так. Это возможно, но проект, который уже включает angular 1.x или backbone, и это лишь некоторые из них, едва ли хочет добавлять React 16 без веской причины.
Начало пути (исследование)
Итак, как работает JSX? Короче говоря, преобразовать синтаксис HTML внутри js в функцию с несколькими аргументами, которые заменяют тег, атрибуты и содержимое, например: <h1 className=”headline”>Hi</h1>
на dom(“h1”, { className: “headline” }, “Hi”)
, поэтому моя идея в то время заключалась в том, могу ли я повторно реализовать то, что должна делать dom (), что будет будь то.
Основные шаги
dom () должен прочитать 3 аргумента, и первый аргумент даст мне имя элемента, который он должен быть создан, а затем const element = document.createElement(arg1)
, похоже, будет подходящим вариантом, если arg1 не является строкой, и в этом случае будет быть функцией из компонента, где <CustomComponent />
будет dom(CustomComponent, null, null)
, и я выполню эту функцию вместо создания нового элемента.
Второй аргумент, с другой стороны, был объектом, поэтому, по крайней мере, для начала базового слияния необходимо сделать работу.
function dom(tag, attrs, ...children) { // Custom Components will be functions if (typeof tag === 'function') { return tag() } // regular html tags will be strings to create the elements if (typeof tag === 'string') { // fragments to append multiple children to the initial node const fragments = document.createDocumentFragment() const element = document.createElement(tag) children.forEach(child => { if (child instanceof HTMLElement) { fragments.appendChild(child) } else if (typeof child === 'string'){ const textnode = document.createTextNode(child) fragments.appendChild(textnode) } else { // later other things could not be HTMLElement not strings console.log('not appendable', child); } }) element.appendChild(fragments) // Merge element with attributes Object.assign(element, attrs) return element } }
Для второго аргумента просто Object.assign(element, arg2)
, и по большей части он это сделал; наконец, для третьего аргумента, если это строка, должна добавить текст узла, который был определяющей частью, потому что, если 3-й аргумент был функцией, просто позвольте циклу начаться снова, Итак, где мы находимся:
- Мы можем создавать новые элементы HTML
- Мы также можем использовать нестандартные компоненты
- Мы можем добавлять классы и другие простые строковые аргументы
- Мы можем добавлять текст или другие элементы как дочерние элементы
- Мы можем отображать списки (братьев и сестер)
Неплохо для основ, вот пример того, что было совместимо на этом этапе, и фиксация:
function Headline() { return ( <h1 className="headline">Inital Line <br /> new line </h1> ) } function Main() { return ( <div> <Headline /> <p>Lorem ipsum</p> <ul> <li><a href="">anchor</a></li> <li>2</li> <li><a href="">anchor2</a> More</li> </ul> </div> ) } const app = document.querySelector('.app') app.appendChild(Main())
Давайте перечислим, чего нам не хватает, по крайней мере для альфа-версии, главные вещи, которые "необходимо реализовать":
Array.map( item => <tag>{item}</tag>)
- Ссылается на реализацию прослушивателей событий или всего, что требует доступа к
HTMLNodeElement
- Фрагменты (у текущего компонента реакции они были, и мне не хотелось переписывать в div)
Приличная реализация (альфа)
Начиная с map, потому что это наиболее часто используемый, проверяя, является ли arg3 массивом, мы должны иметь возможность добавить результат этих функций во фрагмент документа и добавить позже к корню, как, например, в случае нескольких LI корнем будет UL / OL, ясно, теперь братья и сестры являются дополнительными аргументами в функции dom, а не просто массивом в качестве последнего аргумента, я рад, что мы из первых рук знаем, что первый 2 аргумента есть, а также благодаря babel мы можем просто fn(arg1, arg2, …arg3){}
, а arg3 всегда будет массивом даже с одним элементом, поэтому у нас уже есть функция для этого, давайте просто воспользуемся ею повторно. "совершить"
2 Еще функции на вынос
Не хватало только ссылок и фрагментов, поэтому ссылка была такой же простой, как спросить, называется ли одно из свойств второго аргумента ref и передать обратный вызов тому же узлу. Фиксация
На этом этапе у a было приблизительное представление о том, как работает React, и это было хорошее упражнение, только отсутствующие фрагменты, отсутствующая функция для многих версий React, сначала я создал функцию для экспорта и возможности чтобы использовать его в тот момент, я не знал, как должна выглядеть функция. Я только что вернул слово Фрагмент для целей регистрации, забавная вещь, вот и все, после того, как я попытался записать это слово, я понял, что если функция возвращает фрагмент Я должен возвращать только дочерние элементы 😋 (все еще не работает для корневого приложения, но кто экспортирует фрагменты в качестве основных функций? Надеюсь, не много людей 😉)
С этим основным, менее 50 строк вот полный пример того, что вы можете сделать.
import dom, { Fragment } from 'jsx-render' function Headline() { return ( <Fragment> <h1 className="headline">Hello this in an h1 <br /> new line </h1> <h2>Second Headline</h2> </Fragment> ) } function Main() { return ( <div> <Headline /> <p>Lorem ipsum</p> <ul> <li><a href="">anchor</a></li> <li>More</li> </ul> <ol> {items.map(item => <li>{item}</li>)} </ol> <button ref={node => { node.addEventListener('click', console.log) }}> Click Me! </button> </div> ) } const app = document.querySelector('.app') app.appendChild(Main())
Выводы / Советы / Подсказки / Вопросы и ответы
- JSX прекрасно дает вам возможность реализовать свою собственную прагму fn и не так уж и сложен.
- Миграция из компонента React была просто копипастом, потому что компонент не имел состояния, только одна функция щелчка, чтобы открыть модальное окно, но переключение открытого / закрытого класса никому не повредит, пока все хорошо.
- «Если вы реализуете такие события, как насчет зомби?» - Что касается структуры компании, то сейчас компоненты будут использоваться на статичных и очень простых страницах, с SPA нет истории, и поэтому меня это не особо беспокоит.
- Когда взаимодействия действительно просты, как нажатие для переключения, вам не нужно реагировать / сокращать, иногда 1-часовая повторная реализация может сэкономить вам 30 КБ на загрузку.
- Как реализовать фрагменты поражает меня.
Другие подходы
После дополнительных исследований я также нашел библиотеку, которая переносится на JSX, но вместо того, чтобы создавать fn () для каждого компонента для выполнения во время выполнения, он преобразует каждый компонент непосредственно в document.createElement, есть несколько небольших недостающих функций, о которых не стоит беспокоиться если вам не нужна поддержка SVG, о которой мы поговорим / реализуем позже.
Примечания
В примере репо используется lerna на случай, если вы захотите попробовать. Jsx-файлы / чертеж