Определение пользовательских тегов

Riot.js является современным и минималистичным фреймворком для разработки пользовательских интерфейсов на JavaScript, который использует компонентный подход. Основной строительный блок приложения в Riot — пользовательский тег (custom tag), объединяющий разметку, логику и стиль в одном модуле.

Основная структура пользовательского тега

Пользовательский тег в Riot.js создаётся с помощью функции riot.define или, в новых версиях, с использованием синтаксиса <tag> в отдельном файле .tag. Основная структура включает три ключевых секции:

  1. Разметка (markup) Содержит HTML-шаблон компонента. В шаблоне могут использоваться специальные синтаксические конструкции Riot:

    • { expression } — для вывода данных
    • each={items} — для перебора массивов
    • if={condition} — для условного рендеринга
  2. Логика (script) Внутри тега можно определять методы, вычисляемые свойства, слушатели событий и жизненный цикл компонента:

    this.on('mount', () => {
      console.log('Компонент смонтирован');
    });
    this.increment = () => this.count++;
  3. Стили (style) Каждый тег может содержать scoped CSS, который применяется только к этому компоненту:

    <style>
      .counter {
        font-size: 20px;
        color: blue;
      }
    </style>

Создание простого тега

Пример простого компонента счётчика:

<counter>
  <p class="counter">{ count }</p>
  <button oncl ick={ increment }>Увеличить</button>

  <script>
    this.count = 0
    this.increment = () => this.count++
  </script>

  <style>
    .counter {
      font-weight: bold;
      margin-bottom: 10px;
    }
  </style>
</counter>
  • this.count — локальное состояние компонента.
  • oncl ick={ increment } — связывает событие кнопки с методом компонента.
  • Scoped CSS гарантирует, что стили .counter не повлияют на другие элементы страницы.

Жизненный цикл пользовательского тега

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

  • this.on('mount', callback) — вызывается после добавления компонента в DOM.
  • this.on('update', callback) — срабатывает при обновлении состояния.
  • this.on('unmount', callback) — выполняется перед удалением компонента из DOM.

Пример использования:

this.on('mount', () => {
  console.log('Компонент смонтирован');
});

this.on('update', () => {
  console.log('Компонент обновлён');
});

this.on('unmount', () => {
  console.log('Компонент удалён');
});

Свойства и параметры тега

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

<counter initial={5}></counter>

<script>
  this.count = this.props.initial || 0
</script>
  • this.props содержит все переданные атрибуты.
  • Значения по умолчанию можно задавать через логическую конструкцию или метод this.update().

Вложенные теги и повторное использование

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

<app>
  <counter initial={10}></counter>
  <counter initial={20}></counter>
</app>

Каждый counter работает независимо, сохраняя собственное состояние.

События между тегами

Для обмена данными между тегами используется система событий:

this.trigger('countChanged', this.count)

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

<counter oncountChan ged={handleChange}></counter>

<script>
  handleChange = (value) => {
    console.log('Новое значение:', value)
  }
</script>

Композиция и переиспользование стилей

Riot.js поддерживает вложенные стили и наследование через классы. Можно определять общие CSS-классы для нескольких тегов, комбинируя их с scoped стилями:

<counter>
  <style>
    :host {
      display: block;
      margin: 5px;
    }
    .counter {
      color: green;
    }
  </style>
</counter>

:host применяется к корневому элементу тега, облегчая стилизацию контейнера.

Итоги по пользовательским тегам

Пользовательские теги в Riot.js обеспечивают чёткое разделение логики, разметки и стилей, поддержку локального состояния, событий и модульность компонентов. Они позволяют создавать повторно используемые элементы интерфейса, управлять их жизненным циклом и интегрировать с родительскими компонентами через props и события. Такой подход делает разработку интерфейсов более организованной и удобной для масштабирования.