Расширение функционала Stencil

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

Компоненты и декораторы

Каждый компонент Stencil определяется с помощью декоратора @Component, который задает имя компонента, путь к шаблону и стилям:

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

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true
})
export class MyComponent {
  @Prop() title: string;
  @State() count: number = 0;

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

Ключевые моменты:

  • @Prop() — свойства компонента, которые могут передаваться извне.
  • @State() — внутренние реактивные состояния.
  • shadow: true — использование Shadow DOM для изоляции стилей.

Методы жизненного цикла

Stencil предоставляет набор хуков, позволяющих расширять поведение компонентов:

  • componentWillLoad() — вызывается до первого рендера.
  • componentDidLoad() — после первого рендера и вставки в DOM.
  • componentWillUpdate() и componentDidUpdate() — перед и после обновления компонента.
  • componentDidUnload() — перед удалением компонента из DOM.

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

componentWillLoad() {
  console.log('Компонент загружается');
}

componentDidLoad() {
  this.fetchData();
}

События и обработчики

Stencil поддерживает кастомные события с помощью декоратора @Event и эмиссии событий через EventEmitter:

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

@Event() itemSelected: EventEmitter<string>;

selectItem(item: string) {
  this.itemSelected.emit(item);
}

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

<my-component onItemSelec ted="handleSelection"></my-component>

Динамическое расширение с помощью слотов

Слоты (<slot>) позволяют внедрять произвольный контент внутрь компонентов:

render() {
  return (
    <div>
      <slot name="header"></slot>
      <slot></slot>
    </div>
  );
}

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

Директивы и декораторы

Stencil позволяет создавать декораторы свойств и методов для расширения функционала:

  • @Watch(propName) — отслеживание изменений свойства.
  • @Method() — публичные методы компонента, доступные через DOM.
@Watch('title')
watchTitle(newValue: string, oldValue: string) {
  console.log('Title изменился с', oldValue, 'на', newValue);
}

@Method()
async resetCount() {
  this.count = 0;
}

Расширение через утилиты и библиотеки

Stencil интегрируется с внешними библиотеками и утилитами. Возможна генерация динамических компонентов на основе JSON-схем, подключение RxJS для реактивного потока данных, а также использование CSS-in-JS для динамических стилей.

import { Observable } from 'rxjs';

data$: Observable<number>;

componentWillLoad() {
  this.data$.subscribe(value => {
    this.count = value;
  });
}

Плагины и генерация кода

Stencil поддерживает плагины, которые могут изменять процесс компиляции. Можно писать генераторы компонентов, внедрять кастомные трансформации JSX и создавать middleware для обработки атрибутов и стилей.

// пример плагина, который добавляет префикс ко всем тегам
export function prefixPlugin() {
  return {
    transformNode(node) {
      node.tagName = `prefix-${node.tagName}`;
      return node;
    }
  }
}

Оптимизация и lazy loading

Stencil поддерживает ленивую загрузку компонентов (lazy loading), что критично для производительности больших приложений. Компоненты автоматически разделяются на чанки, и браузер подгружает их только при необходимости. Этот механизм можно расширять с помощью кастомных условий подгрузки, например, Intersection Observer.

@Component({
  tag: 'lazy-component',
  shadow: true,
  assetsDirs: ['assets']
})
export class LazyComponent {}

Интеграция с фреймворками

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

import { defineCustomElements } from 'my-component-library/loader';
defineCustomElements(window);

Сборка и конфигурация

Расширение возможностей Stencil часто связано с настройкой stencil.config.ts:

  • rollupPlugins — подключение сторонних плагинов Rollup.
  • outputTargets — конфигурация для генерации компонентов в формате React, Angular или as Custom Elements.
  • serviceWorker — добавление PWA-функционала.
export const config: Config = {
  namespace: 'my-lib',
  outputTargets: [
    { type: 'dist' },
    { type: 'www', serviceWorker: null }
  ]
};

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