Щелкните здесь, чтобы опубликовать эту статью в LinkedIn »

Запуск проекта был легкой частью, так как каждый проект узла 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-файлы / чертеж