Custom Elements

Stencil предоставляет мощный механизм для создания и использования custom elements (кастомных элементов) в веб-приложениях. Это стандарт веб-компонентов, который позволяет разработчикам создавать повторно используемые, инкапсулированные компоненты с минимальной зависимостью от внешних библиотек. В Stencil процесс создания кастомных элементов интуитивно понятен, при этом он предлагает высокую производительность и совместимость с существующими фреймворками и библиотеками.

Основы кастомных элементов

Кастомные элементы представляют собой пользовательские HTML-элементы, которые можно использовать в любом HTML-документе так же, как и стандартные элементы, такие как <div>, <input>, или <button>. Для создания кастомного элемента используется API CustomElementRegistry, которое позволяет зарегистрировать новый элемент и привязать к нему логику. В Stencil эта логика упрощена и организована в виде компонентов, которые можно настраивать через атрибуты и события.

Структура компонента

Компонент в Stencil — это класс, который расширяет базовый класс Component. Он может быть использован для создания кастомного элемента, который затем может быть зарегистрирован в глобальном реестре. Рассмотрим базовую структуру компонента:

import { Component, h } from '@stencil/core';

@Component({
  tag: 'my-custom-element',
  styleUrl: 'my-custom-element.css',
  shadow: true
})
export class MyCustomElement {
  render() {
    return (
      <div>
        <h1>Пример кастомного элемента</h1>
        <p>Это кастомный элемент, созданный с использованием Stencil.</p>
      </div>
    );
  }
}

Атрибуты и свойства компонента

Атрибуты позволяют передавать данные в кастомный элемент через HTML-разметку. В Stencil для работы с атрибутами используются декораторы. Атрибуты компонента могут быть связан с его внутренними свойствами через декоратор @Prop. Стандартное использование атрибутов позволяет гибко управлять состоянием компонента, например:

import { Component, Prop, h } from '@stencil/core';

@Component({
  tag: 'user-card',
  styleUrl: 'user-card.css',
  shadow: true
})
export class UserCard {
  @Prop() name: string;
  @Prop() age: number;

  render() {
    return (
      <div>
        <h2>{this.name}</h2>
        <p>Возраст: {this.age}</p>
      </div>
    );
  }
}

Теперь можно использовать компонент <user-card> в HTML-разметке и передавать ему значения:

<user-card name="Иван" age="30"></user-card>

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

Слушатели событий

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

import { Component, Event, EventEmitter, h } from '@stencil/core';

@Component({
  tag: 'my-button',
  styleUrl: 'my-button.css',
  shadow: true
})
export class MyButton {
  @Event() clicked: EventEmitter;

  handleClick() {
    this.clicked.emit({ message: 'Кнопка была нажата!' });
  }

  render() {
    return (
      <button onCl ick={() => this.handleClick()}>Нажми меня</button>
    );
  }
}

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

<my-button onclic ked="handleButtonClick"></my-button>

<script>
  function handleButtonClick(event) {
    console.log(event.detail.message);
  }
</script>

Shadow DOM

Stencil поддерживает использование Shadow DOM, который позволяет инкапсулировать стиль и структуру компонента, предотвращая их воздействие на внешний контекст. Включение Shadow DOM для компонента можно настроить через параметр shadow в декораторе @Component. Это дает возможность избежать конфликтов стилей между компонентами.

Включение Shadow DOM в Stencil:

@Component({
  tag: 'my-shadow-component',
  styleUrl: 'my-shadow-component.css',
  shadow: true
})
export class MyShadowComponent {
  render() {
    return (
      <div>
        <h2>Текст в Shadow DOM</h2>
      </div>
    );
  }
}

С помощью Shadow DOM можно полностью изолировать стили и структуру компонента от окружающего контекста, что полезно при разработке модульных и независимых компонентов.

Стилизация кастомных элементов

Stencil позволяет добавлять стили к компонентам с помощью обычных CSS-файлов, а также с использованием CSS-переменных и стилевых инкапсуляций. Для стилей, применяемых внутри компонента, используется параметр styleUrl, который позволяет указать путь к CSS-файлу. Также можно использовать scoped стили, которые будут применяться только к компоненту, если включен режим Shadow DOM.

Пример:

@Component({
  tag: 'my-styled-component',
  styleUrl: 'my-styled-component.css',
  shadow: true
})
export class MyStyledComponent {
  render() {
    return (
      <div>
        <h1>Стильный компонент</h1>
      </div>
    );
  }
}

При этом в my-styled-component.css могут быть определены локальные стили, которые будут применяться только к элементам внутри этого компонента.

Смешивание с другими фреймворками

Один из важных аспектов Stencil — это его способность интегрироваться с другими фреймворками. Созданные компоненты можно использовать в любых фреймворках, таких как React, Angular, Vue или даже в чистом JavaScript. Stencil автоматически генерирует соответствующие оболочки для использования компонентов в этих фреймворках, что позволяет строить кросс-фреймворк-приложения.

Для интеграции с React, например, можно использовать следующий код:

import { Component, Prop, h } from '@stencil/core';

@Component({
  tag: 'my-react-component',
  styleUrl: 'my-react-component.css',
  shadow: true
})
export class MyReactComponent {
  @Prop() title: string;

  render() {
    return <h1>{this.title}</h1>;
  }
}

После сборки компонента с помощью Stencil, его можно будет использовать в React-приложении как обычный компонент:

import React from 'react';
import { render } from 'react-dom';

import 'my-react-component';

const App = () => (
  <my-react-component title="Привет, мир!"></my-react-component>
);

render(<App />, document.getElementById('root'));

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

Заключение

Использование кастомных элементов в Stencil открывает широкие возможности для создания высокопроизводительных, инкапсулированных и повторно используемых компонентов. Система атрибутов, событий, поддержки Shadow DOM и интеграции с другими фреймворками позволяет разработчикам легко строить модульные и масштабируемые веб-приложения.