Введение в StencilJS: компилятор, который генерирует веб-компоненты.
Что такое StencilJS?
StencilJS - это библиотека для генерации веб-компонентов, созданная командой разработчиков Ionic Framework.
Как вы, возможно, знаете, когда изначально был выпущен Ionic, он был создан специально для Angular. Затем начали появляться другие фреймворки, которые заняли большую часть фронтенд-сообщества - и Ionic хотел, чтобы его инструменты использовались кем угодно, независимо от выбранной фреймворка.
Переписать одни и те же компоненты для всех основных фреймворков было бы невыполнимой (и, вероятно, неправильной) задачей. Веб-компоненты позволяют решить эту проблему - но не без ограничений, которые все хорошо задокументированы.
Stencil пытается предоставить абстракцию поверх веб-компонентов, чтобы упростить их написание и доставку.
Одна из самых интересных особенностей Stencil заключается в том, что скомпилированный код поставляется без Stencil (как и Svelte, еще один «исчезающий» фреймворк): это позволяет компонентам быть невероятно легкими и делает их идеальными для использования. используется с другими фреймворками, такими как React, Vue или Angular.
Именно из-за их легкости и встроенной поддержки браузеров веб-компоненты все чаще используются для систем проектирования. Их можно даже включить в существующие модульные библиотеки компонентов React / Vue / Angular, созданные с использованием таких инструментов, как Bit, поскольку каждый компонент в этой библиотеке полностью независим.
Так, например, вы можете отправить недавно созданные веб-компоненты в свою коллекцию битов (библиотеку), которая до сих пор содержала только компоненты React (и позволить вашей команде использовать их в ваших проектах React).
Почему я выбрал трафарет для своего проекта
Недавно я занялся личным проектом, который в последнее время отнимает у меня много времени.
Когда мне пришлось выбирать, какие инструменты использовать, я начал исследовать библиотеку для создания коллекции компонентов со следующими условиями:
- Это должно было быть очень быстро
- Он должен был быть очень легким и простым
- Он должен был быть максимально ориентированным на будущее
- Это должно было быть поддержано активным и динамичным сообществом.
Мой вариант использования - создание сторонних виджетов, которые будут загружаться на веб-сайты пользователя. Как вы понимаете, важны производительность и вес. Никто не хочет, чтобы третья сторона замедляла работу их веб-сайта!
Возможные кандидаты
Помимо Stencil, я начал исследовать различные другие инструменты:
- Angular Elements (фреймворк, который я знаю лучше всего, поэтому это был очень серьезный кандидат), Svelte, Preact и Lit-Element.
Хотя приведенные выше варианты являются чрезвычайно допустимыми и, вероятно, также очень хорошо сработали бы для моего варианта использования, я решил использовать Stencil по следующим причинам:
- JSX: любите вы или ненавидите, он очень хорошо известен и используется во многих других фреймворках. Кто угодно мог забрать его в считанные часы.
- Небольшие и умные пакеты: все компоненты загружаются лениво и будут использовать правильный пакет для используемого браузера благодаря дифференциальной загрузке
- Первоклассная поддержка машинописного текста - это было для меня очень важно
- Несмотря на то, что я использовал JSX в качестве языка шаблонов, как в первую очередь пользователь Angular, я обнаружил, что начать работу с ним чрезвычайно легко, и почти все с самого начала имело смысл. Есть некоторые вещи, на которые следует обратить внимание, как мы увидим в следующих разделах.
После того, как я с большим удовольствием использовал Stencil, я решил написать эту статью, чтобы познакомить вас с этим инструментом и поделиться своим опытом.
Строительные блоки StencilJS
В этом разделе мы увидим, как создать его с помощью Stencil, начиная с основ. Чтобы понять, как работает Stencil, мы рассмотрим его наиболее важные темы:
- Определение компонента
- Передача свойств
- Обработка внутреннего состояния
- Испускание событий
- Методы экспонирования
- Создание шаблонов с помощью JSX и слотов
Как только вы освоите эти концепции, вы сможете приступить к написанию компонентов Stencil за очень короткое время! Да, это так просто.
Анатомия компонента трафарета
Компонент трафарета объявляется с помощью декоратора Component
; да, это может быть знакомо, это действительно похоже на Angular.
Мы определяем:
- имя его тега со свойством
tag
- стили компонента с использованием свойства
styleUrls
- функция с именем
render
, отвечающая за определение шаблона с помощью JSX. И снова да, это знакомо, потому что он работает аналогично компонентам класса React.
// single-choice.tsx import { Component, h } from '@stencil/core'; @Component({ tag: 'single-choice', styleUrls: ["./single-choice.css"] }) export class SingleChoiceComponent { render() { return 'I will be a single choice field!'; } }
Примечание: импорт h необходим, если мы используем JSX в функции рендеринга.
После того, как мы создадим компонент с помощью компилятора Stencil и импортируем скрипты на нашу веб-страницу, мы можем просто вызвать компонент, как и с обычным элементом HTML:
<single-choice></single-choice>
Объемная и собственная теневая модель DOM
Веб-компоненты могут быть ограничены с помощью Shadow DOM (с помощью свойства shadow
), но это по-прежнему не поддерживается во всех браузерах (такие как IE11 и Safari поддерживают его лишь частично).
Так же, как инкапсуляция эмулируемого представления Angular, Stencil предоставляет свойство под названием scoped
, которое будет имитировать такое же поведение и инкапсулировать стиль наших компонентов.
По умолчанию я всегда устанавливаю scoped
вместо shadow
.
@Component({ tag: 'single-choice', scoped: true })
Передача свойств компонентам
Передача свойств компонентам также во многом напоминает работу Angular. Чтобы передать свойства, мы можем определить свойства класса и украсить их декоратором Prop
.
import { ..., Prop } from "@stencil/core"; @Component({...}) export class SingleChoiceComponent { @Prop() id: string; render() { return ( <label for={this.id}></label> ); } }
Однако этот декоратор может принимать некоторую конфигурацию, о которой вы можете не знать:
- атрибут: имя передаваемого атрибута, если имя свойства класса должно быть другим.
- изменяемый: по умолчанию свойства неизменяемы. Если он установлен извне компонента, его нельзя изменить, если только мы явно не установим для этого свойства значение
true
. - отражать: если мы хотим предоставить атрибут в DOM компонента, мы можем установить это свойство, чтобы мы могли получить доступ к свойству извне.
@Component({...}) export class SingleChoiceComponent { @Prop({ attribute: 'id', mutable: true, reflect: true }) fieldId: string; render() {...} }
Теперь, когда мы предоставили свойство id
, мы можем получить к нему доступ с помощью DOM API:
const singleChoice = document.querySelector('single-choice'); const id = singleChoice.id;
Внутреннее состояние
Stencil пытается максимизировать производительность и эффективность за счет повторного рендеринга только при необходимости. Если вы привыкли к таким фреймворкам, как Angular или Svelte, вы можете не сразу понять, почему ваш компонент не обновляет свое представление.
Чтобы запустить повторную визуализацию при изменении свойства, Stencil предоставляет декоратор State
:
@Component({...}) export class SingleChoiceComponent { @Prop() id: string; @State() value: string; render() {...} }
Примечание. Если мы забудем украсить свойство value
, представление не будет повторно отображено.
События
Конечно, компоненты также могут открывать события своим родителям, чтобы обеспечить общение между родителями и детьми.
Событие определяется следующим образом:
- мы импортируем декоратор
Event
и устанавливаем его относительное свойство - мы определяем его тип, это
EventEmitter
@Component({...}) export class SingleChoiceComponent { @Prop() id: string; @State() value: string; @Event() valueChanged: EventEmitter<Option>; onClick(option: Option) { this.valueChanged.emit(option); } render() {...} }
Если вы используете событие с дочерним элементом, отображаемым в функции рендеринга, вы можете просто передать событие и вызвать метод:
<parent-component> <single-choice valueChanged={(e) => this.choiceSelected(e)) ></single-choice>; </parent-component>
В случаях, когда наш дочерний компонент вложен глубоко в дочерние элементы родителя, мы можем прослушивать пользовательские события благодаря декоратору Listen
.
class ParentComponent { @Listen('valueChanged') valueChanged(value) { // do something with value } }
Методы
Методы компонента не следует путать с методами, которые вы обычно определяете в классе компонента. Stencil позволяет предоставлять определенные методы как общедоступный API, украсив их декоратором Method
.
@Component({...}) export class SingleChoiceComponent { @Prop() id: string; @State() value: string; @Method() async getValue() { return this.value; } }
Как только метод определен, мы можем вызвать его извне:
const singleChoice = document.querySelector('single-choice'); const value = singleChoice.getValue();
Проблемы:
- Публичные методы всегда должны быть
async
- Не рекомендуется использовать общедоступные методы для раскрытия источника достоверности компонента. Предлагается вместо этого полагаться исключительно на события и реквизит.
Шаблоны: JSX и слоты
Если вы когда-либо работали с React, Preact или любой другой библиотекой, использующей JSX, вам не так много нового, чтобы научиться использовать Stencil. Если нет, то есть чему поучиться, но, к счастью, JSX довольно прост.
Конечно, вы также можете определить функциональные компоненты и использовать их в функции рендеринга:
const Label = (_, text: JSX.Element) => <label>{text}</label> @Component({...}) export class SingleChoiceComponent { render() { return ( <Label> <slot /> </Label> ); } }
Как вы можете видеть выше, слоты могут быть полезны для визуализации содержимого компонента. Вы также можете определить именованные слоты, чтобы контролировать, где будет отображаться контент.
class MyComponent() { render() { return ( <div> <slot name="heading"> </div> ); } } <my-component> <h1 slot="heading">Heading</h1> </my-component>
Заключительные слова
StencilJS - отличная библиотека для работы. Поскольку я работаю в основном с Angular, я привык к действительно хорошему опыту разработчика и богатой экосистеме, но Stencil не подвел меня в этом отношении.
Это очень правильный выбор, если вы хотите дополнить свои приложения набором многократно используемых компонентов. Как упоминалось ранее, вы можете постепенно создавать дизайн-систему, состоящую из веб-компонентов, или заменять существующую (реализованную с помощью некоторой инфраструктуры) с помощью таких инструментов, как Bit. Это обеспечит перспективу вашей системы проектирования и сделает ее доступной для всех интерфейсных технологий, используемых в вашей компании. Это также правильный выбор, если вы используете простой Javascript, поскольку добавляемые накладные расходы крайне минимальны.
Конечно, я иногда сталкиваюсь с ошибками, но команда, как правило, очень отзывчива и очень часто выпускает новые версии. Использование компонентов Ionic в качестве эталона также является отличным способом понять передовой опыт и увидеть, как основная команда подошла к своей архитектуре.
Ожидайте от меня больше статей о Stencil в будущем!
Ресурсы
- Штатный туннель трафарет
- Stencil Redux
- Нетривиальные примеры компонентов Stencil из исходного кода фреймворка Ionic
Если вам нужны какие-либо разъяснения, или если вы считаете, что что-то неясно или неправильно, оставьте, пожалуйста, комментарий!
Надеюсь, вам понравилась эта статья! Если да, подпишитесь на меня в Medium, Twitter или на моем веб-сайте, чтобы увидеть больше статей о разработке программного обеспечения, Front End, RxJS, Typescript и многом другом!