reacton

0

Описание

Реактивные Веб-компоненты

Языки

  • JavaScript94%
  • HTML5,9%
  • Остальные0,1%
год назад
год назад
год назад
год назад
год назад
год назад
год назад
год назад
год назад
год назад
README.md

EN / RU

Реактивные Веб-компоненты


reacton

GitHub | GitFlic | GitVerse | NpmJS | Скачать⤵️


Reacton (сокр. Rtn) – это фреймворк JavaScript для быстрого создания реактивных Веб-компонентов. Он поддерживает все методы и свойства, которые предоставляются стандартными Веб-компонентами. Кроме этого, фреймворк содержит ряд дополнительных методов и реализует рендеринг Веб-компонентов на стороне сервера.


Ниже представлен пример создания простого компонента:


  1. Быстрый старт
  2. Состояние компонента
  3. Циклы
  4. Примеси
  5. Виды
  6. Реактивные свойства
  7. Статические свойства
  8. Специальные методы
  9. Эмиттер событий
  10. Маршрутизатор
  11. Серверный рендеринг



Быстрый старт


Для создания компонентов применяются классы. Классы могут быть как встроенными в основной скрипт, так и импортированы из внешнего модуля. Создайте новый рабочий каталог, например, с названием app, и скачайте в этот каталог файл rtn.global.js.

Добавьте в каталог файл index.html со следующим содержимым:

Чтобы гарантировать отсутствие конфликтов имён между стандартными и пользовательскими HTML-элементами, имя компонента должно содержать дефис «-», например, "my-element" и "super-button" – это правильные имена, а "myelement" – нет.

В большинстве примеров этого руководства, префикс будет состоять из одной буквы «w-». т.е. компонент Hello будет называться "w-hello".

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

Открыв файл index.html в браузере, на экране отобразится созданное в компоненте Hello сообщение:

Привет, Reacton!


Компоненты можно выносить в отдельные модули. В этом случае, файл компонента Hello выглядел бы как показано ниже:

Для работы с внешними компонентами, вам потребуется любой разработочный сервер, такой, например, как lite-server.

Установить данный сервер можно с помощью команды в терминале:

npm install --global lite-server

Запуск сервера из каталога, в котором находится приложение, осуществляется с помощью команды в терминале:

lite-server

Кроме этого, фреймворк поддерживает однофайловые компоненты, которые могут быть использованы наравне с модульными, при создании проекта в системе сборки webpack.

Ниже показан пример простого однофайлового компонента:

Однофайловый компонент должен присваивать свой класс переменной exports. Эта переменная будет автоматически объявлена во время создания структуры компонента в системе сборки проекта.

В однофайловых компонентах можно использовать инструкцию import, например:


Однофайловые компоненты позволяют выделить HTML-разметку из логики компонента. Однако, такие компоненты не могут работать в браузере напрямую. Они требуют специального обработчика, который подключается в webpack.

Чтобы иметь возможность работать в браузере с компонентами, в которых логика отделена от HTML-содержимого, существуют встроенные компоненты.

Ниже показан пример простого встроенного компонента:

Встроенный компонент должен возвращать свой класс, а содержимое его тега <script> можно рассматривать как функцию. Однако, встроенные компоненты не подходят для рендеринга на стороне сервера и, кроме этого, в них нельзя использовать инструкцию import, но допускается использование выражения import(), например:


Для быстрого доступа к компоненту, достаточно добавить идентификатор к элементу, который подключает компонент к документу, как показано ниже:

Теперь откройте консоль браузера и последовательно введите команды:

hello.$state.message = 'Веб-компоненты' hello.$state.color = 'blue'

Цвет и содержимое заголовка изменятся:

Привет, Веб-компоненты!



Состояние компонента


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

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


Методы компонента не являются состоянием. Они предназначены для выполнения действий с состоянием компонента и хранятся в прототипе объекта состояния:


Для доступа к объекту состояния, применяется специальное свойство $state. С помощью этого свойства, можно получить или присвоить новое значение состоянию, как показано ниже:

hello.$state.message = 'Веб-компоненты'

Обновление содержимого компонента на основе нового состояния происходит автоматически.


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

В примере ниже, обработчик элемента <h1> будет работать и после обновления состояния компонента. Поскольку обновление изменит только старое значение его атрибута и текстового содержимого:



Циклы


Reacton поддерживает три вида циклов «for», которые реализованы в JavaScript. Все они определяются с помощью специального атрибута $for и выводят содержимое своих HTML-элементов столько раз, сколько предусмотрено условием цикла.

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

В примере ниже, цикл «for» выводит 10 параграфов с числами от 0 до 9:

В специальном атрибуте $for нельзя использовать операторы определения переменных: var, let и const соответственно. Это приведёт к ошибке:


Цикл «for-in» используется для вывода содержимого объектов, как показано ниже:


Цикл «for-of» предназначен для работы с массивами:


При использовании событий в циклах с помощью специального атрибута @event, они будут использовать актуальное значение переменной цикла для своей фазы итерации:

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


Можно применять циклы с любой глубиной вложенности:



Примеси


Примесь – общий термин в объектно-ориентированном программировании: класс, который содержит в себе методы для других классов. Эти методы могут использовать разные компоненты, что позволяет не создавать методы с одинаковым функционалом для каждого компонента отдельно.

В примере ниже, метод printName() из примеси используют компоненты Hello и Goodbye:



Виды


Для отображения различных компонентов, используется специальный атрибут $view. Этот атрибут может быть назначен любому элементу, но обычно используется элемент DIV. Содержащий атрибут элемент заменяется на компонент, название которого содержится в значении этого атрибута, например:

Атрибут $view нельзя использовать вместе с циклами. Пример ниже приведёт к ошибке:



Реактивные свойства


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

Для вставки реактивных свойств в текстовые узлы, применяются двойные фигурные скобки:

Чтобы вставить реактивное свойство в атрибут, перед его именем необходимо поставить символ двоеточия:

В примере ниже, реактивное свойство добавляется логическому атрибуту:

Двоеточие перед названием атрибута используется лишь в HTML-разметке шаблона компонента, для указания на то, что этот атрибут принимает реактивное свойство. После компиляции, в итоговой разметке компонента будут отображаться названия атрибутов без двоеточий.


Для атрибутов событий, перед названием атрибута указывается символ @, за которым следует название события без префика on, как показано ниже:

Вместо непосредственного изменения реактивного свойства в атрибуте события, в него можно передать название метода, который изменяет реактивное свойство, например:


В атрибутах событий доступен объект event, с помощью свойства target которого, можно получить ссылку на элемент, на котором произошло событие:

Атрибуты событий могут иметь такие же параметры, что передаются в третьем аргументе методу addEventListener. Эти параметры указываются через точку, после названия события:

В примере ниже, вызывающий событие элемент будет показан в консоли только один раз:



Статические свойства


alias – это статическое свойство позволяет добавить псевдоним для ключевого слова this, т.е. для контекста объекта состояния:

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


time – это статическое свойство позволяет добавить таймер обновления компонента, если задать ему значение "true", как показано ниже:

Время обновления компонента в миллисекундах отображается в консоли, после каждого изменения любого свойства объекта состояния.


name – это статическое свойство используется, например, когда функции Rtn передаётся анонимный класс, как показано ниже:


mode – это статическое свойство отвечает за добавление компоненту Теневого DOM. Оно может содержать два значения: "open" или "closed". В последнем случае, когда компонент является закрытым, то невозможно получить доступ из консоли к свойствам его объекта состояния, методам выборки элементов и обновления содержимого.

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

Только компоненты имеющие Теневой DOM, могут содержать локальные стили.


extends – это статическое свойство отвечает за создание модифицированных компонентов, т.е. таких, которые встраиваются в стандартные HTML-элементы, например:

Свойство должно содержать название встраиваемого элемента, а сам встраиваемый элемент, должен содержать атрибут is со значением, равным названию встраиваемого в него компонента.


serializable – это статическое свойство отвечает за сериализацию Теневого DOM компонента с помощью метода getHTML(). По умолчанию имеет значение "false".


template() – это статическое свойство возвращает будущее HTML-содержимое компонента в виде строки:


startConnect() – этот статический метод выполняется в самом начале подключения компонента к документу, до формирования HTML-содержимого компонента и вызова статического метода connected(), но после создания объекта состояния компонента.

В нём можно инициализировать свойства объекта состояния имеющимися значениями:

или получить данные с сервера для их инициализации. Но в этом случае, метод должен быть асинхронным:

Это единственный статический метод, который можеть быть асинхронным.


connected() – этот статический метод выполняется в самом конце подключения компонента к документу, после формирования HTML-содержимого компонента и вызова статического метода startConnect().

В нём можно добавлять обработчики событий внутренним элементам компонента:

Этот и все последующие статические методы, являются сокращениями стандартных статических методов компонента.


disconnected() – этот статический метод выполняется при удалении компонента из документа.

adopted() – этот статический метод выполняется при перемещении компонента в новый документ.

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

attributes – этот статический массив содержит имена отслеживаемых атрибутов, например:


Все статические методы вызываются в контексте прокси объекта состояния компонента. Это означает, что если необходимое свойство не обнаруживается в объекте состояния, то поиск происходит в самом компоненте.

В примере ниже, свойства id не существует в объекте состояния компонента. Поэтому оно запрашивается из самого компонента:



Специальные методы


Все специальные методы и свойства начинаются с символа доллара «$», за которым следует название метода или свойства.

$() – этот специальный метод выбирает элемент из содержимого компонента по указаному селектору, например, для добавления элементу обработчика события:

Этот метод выбирает содержимое закрытых компонентов, только если он вызывается из статических методов класса компонента.


$$() – этот специальный метод выбирает все элементы из содержимого компонента по указаному селектору, например, для добавления элементам обработчиков событий при переборе их в цикле:

Этот метод выбирает содержимое закрытых компонентов, только если он вызывается из статических методов класса компонента.


$entities() – этот специальный метод обезвреживает строку, содержащую HTML-содержимое полученное из недостоверных источников. По умолчанию, экранируется символ амперсанда «&», символы меньше «<» и больше «>», двойные «"» и одинарные кавычки «'», например:

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

Этот метод доступен в качестве свойства функции Rtn, как показано ниже:

или именованного импорта, когда используется модульная версия фреймворка:


Специальные методы: $event(), $router() и $render(), будут рассмотрены в следующих разделах. Как и для метода $entities(), для них так же имеются свои именованные импорты:

Функция Rtn всегда импортируется по умолчанию.


$state – это специальное свойство ссылается на прокси объекта состояния компонента. Это означает, что если необходимое свойство не обнаруживается в объекте состояния, то поиск происходит в самом компоненте.

В примере ниже, свойства id не существует в объекте состояния компонента. Поэтому оно запрашивается из самого компонента:


$host – это специальное свойство ссылается на элемент, который подключает компонент к документу, т.е. на элемент компонента. Это может пригодиться, если свойства с одинаковыми именами имеются и объекте состояния и в компоненте.

Прокси объекта состояния изначально ищет свойство в самом объекте состояния, это значит, что для получения одноимённого свойства из элемента компонента, необходимо использовать специальное свойство $host, как показано ниже:


$shadow – это специальное свойство ссылается на Теневой DOM компонента:

hello.$shadow

Для закрытых компонентов и компонентов без Теневого DOM, это свойство возвращает значение "null".


$data – это специальное свойство ссылается на объект dataset компонента, который используется для доступа к пользовательским атрибутам, например:


$props – это специальное свойство ссылается на объект состояния родительского компонента, когда в дочерний компонент передаётся специальный атрибут $props без значения:

Специальный атрибут $props здесь указывается без какого-либо значения:

Для доступа к свойствам объекта состояния родительского компонента в HTML-разметке дочернего, используется специальное свойство $props, как показано ниже:

Если необходимо передать из родительского компонента только некоторые свойства, а не весь объект состояния целиком, то специальный атрибут $props должен содержать значение, в котором через запятую указываются названия передаваемых свойств:

Можно изменять состояние внешних компонентов через специальное свойство $props внутренних, но не наоборот. Поскольку здесь используется односторонняя связь между компонентами:

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



Эмиттер событий


Чтобы компоненты могли взаимодействовать между собой и обмениваться друг с другом данными, применяются пользовательские события. Для создания пользовательских событий, используется специальный метод $event(), который доступен в качестве свойства функции Rtn.

Если метод вызывается как конструктор, то он возвращает новый объект эмиттера, который будет генерировать и отслеживать пользовательские события, например:

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

Когда метод $event() вызывается как обычная функция, то в первом аргументе он получает эмиттер, во втором передаётся название пользовательского события, а в третьем аргументе можно передавать любые данные:

Эти данные затем будут доступны в обработчике пользовательского события, в качестве свойства detail объекта события Event, как показано ниже:

В системе сборки webpack, эмиттер можно экспортировать из отдельного модуля, например, из файла Events.js:

для последующего импорта эмиттера в файлы компонентов, которые будут его использовать:


В примере ниже, каждой кнопке из компонента Hello добавляется обработчик события "click", внутри которого, происходит запуск соответствующего пользовательского события объекта эмиттера.

Для отслеживания пользовательских событий, эмиттеру назначаются соответствующие обработчики в компоненте Colors. В последнем обработчике, через свойство detail объекта события Event, свойству состояния присваивается новый массив:



Маршрутизатор


В основе маршрутизатора лежат пользовательские события. Для создания маршрутных событий, используется специальный метод $router(), который доступен в качестве свойства функции Rtn.

Если метод вызывается как конструктор, то он возвращает новый объект эмиттера с переопределённым методом addEventListener(), который будет генерировать и отслеживать маршрутные события, например:

Когда метод $router() вызывается как обычная функция, то в первом аргументе он получает эмиттер, во втором передаётся название маршрутного события, а в третьем аргументе можно передавать любые данные:

В реальном приложении, название маршрутного события указывается не непосредственно, как в примере выше, а берётся из значения атрибута href ссылки, по которой был произведён клик, например:


Передаваемые в последнем аргументе метода $router() пользовательские данные, будут доступны в обработчике маршрутного события, в качестве свойства detail объекта события Event, как показано ниже:

Начальный слэш «/» в названии маршрутного события не является обязательным:

Вся остальная часть названия маршрутного события, кроме начального слэша, должна до конца совпадать со значением атрибута href ссылки, после нажатия на которую, сработает соответствующий этому значению обработчик:


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

Чтобы не приходилось дважды использовать символ обратного слэша в обычной строке для экранирования специальных символов регулярных выражений, можно воспользоваться теговой функцией raw() встроенного объекта String, заключив название маршрутного события в шаблонную строку, например:

или так:


Кроме свойства detail, объект события Event имеет дополнительное свойство params, для получения параметров маршрутов, как показано ниже:

Этот обработчик будет выполняться для всех ссылок вида:

тогда catId будет иметь значение 5, а prodId значение 7.

Для поддержки параметров запросов, объект события Event имеет дополнительное свойство search, которое является короткой ссылкой на свойство searchParams встроенного класса URL, например:

Этот обработчик будет выполняться для всех ссылок вида:

тогда catId будет иметь значение 5, а prodId значение 7.

Последнее дополнительное свойство объекта события Event называется url, оно является объектом встроенного класса URL и помогает разобрать запрос на составляющие:


Ниже показан пример создания простого маршрутизатора для трёх компонентов страниц:

Для обработки маршрутов этих страниц, эмиттеру маршрутизатора назначается обработчик с необязательным параметром маршрута в компоненте Content:

Для того, чтобы этот обработчик срабатывал сразу при открытии приложения и подключал соответствующий маршруту компонент страницы, в конце статического метода connected(), инициируется событие для адреса текущего маршрута из свойства href объекта location:

Остальные компоненты страниц загружаются при клике по соответствующей им ссылке в компоненте Menu:



Серверный рендеринг


SSR (Server Side Rendering) – это методика разработки, при которой содержимое веб-страницы отрисовывается на сервере, а не в браузере клиента. Для рендеринга содержимого веб-страниц используется метод render(), который доступен в качестве свойства функции Rtn. Этот метод работает как на стороне сервера, так и в браузере клиента.

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

Также этот метод доступен в качестве именованного импорта, при использовании модульной версии фреймворка:

Метод возвращает промис, который разрешается после того, когда HTML-содержимое всех используемых компонентов для текущего маршрута приложения будет доступно:

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


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

В примере ниже, содержимое документа выводится начиная с элемента BODY:

По умолчанию, метод выводит очищенное HTML-содержимое документа, т.е. такое, в которых удалены теги: STYLE, SCRIPT и TEMPLATE. Чтобы метод выводил полное HTML-содержимое, необходимо передать ему объект с параметром clean и значением "false", как показано ниже:

Во всех примерах выше, передаваемое в слот содержимое выводилось без самих тегов SLOT. Чтобы передаваемое содержимое выводилось внутри этих тегов, т.е. в полном соответствии со структурой расположения данного содержимого в компоненте, методу необходимо передать объект с параметром slots и значением "true", например:

Все три параметра можно передавать одновременно:


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

npm i

Для запуска приложения в режиме разработки, используется команда:

npm start

а для финальной сборки, со всеми минимизациями приложения в режиме продакшен, команда:

npm run build

Это обычный проект с использованием менеджера задач Gulp и сборщика модулей Webpack. Код сервера находится в файле app.js, а сам сервер написан с помощью фреймворка Express.

Файл сервера представляет собой типичное приложение на фреймворке Express:


Чтобы метод render() мог работать на сервере, используется jsdom – это реализация Веб-стандартов на JavaScript.

Обычным пользователям не нужно отдавать отрендеренное содержимое страницы. Оно необходимо только поисковым ботам и другим автоматическим системам учёта анализа HTML-содержимого. Список этих систем находится в массиве, который можно пополнить дополнительными названиями:

Если в заголовке запроса будет присутствовать любое из этих названий, то сервер будет отдавать отрендеренное HTML-содержимое:

Для всех остальных запросов, сервер будет возвращать файл index.html, который является единственным html-файлом в этом одностраничном приложении:


Рендеринг осуществляется с помощью функции _$RtnRender_() глобального объекта window, как показано ниже:

Эта функция назначается объекту в файле index.js, который является главным файлом всего приложения:


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

Объект XMLHttpRequest можно обернуть в функцию и затем, вызывать эту функцию вместо того, чтобы каждый раз писать код запроса этого объекта вручную, как показано в файле helpers.js, например:


После установки всех зависимостей приложения из файла package.json, для запуска сервера в обычном режиме, необходимо открыть консоль из каталога /server, и ввести в ней следующую команду:

node app

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

node app test