Структура Riot приложения

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

Компоненты и их организация

Компонент в Riot представляет собой файл с расширением .riot. Основная структура файла включает три ключевых блока:

  1. Шаблон HTML — описывает визуальное представление компонента.
  2. Скрипт JavaScript — содержит логику компонента, обработчики событий и реактивные свойства.
  3. Стили CSS — оформляет внешний вид компонента, при этом стили изолированы от глобального пространства.

Пример базового компонента:

<my-component>
  <p>{ message }</p>

  <script>
    export default {
      message: 'Привет, Riot!'
    }
  </script>

  <style>
    p {
      color: #2c3e50;
      font-size: 16px;
    }
  </style>
</my-component>

Выделение ключевых моментов:

  • Все данные, которые используются в шаблоне, определяются в объекте export default.
  • Доступ к DOM осуществляется через обычные методы JavaScript, однако рекомендуется использовать реактивные свойства для обновления интерфейса.
  • Стили в <style> автоматически применяются только к текущему компоненту, исключая конфликты с другими частями приложения.

Иерархия компонентов

Riot.js поддерживает вложенные компоненты. Родительский компонент может импортировать дочерний и использовать его как HTML-тег:

<parent-component>
  <child-component message="Привет из родителя"></child-component>

  <script>
    import ChildComponent from './child-component.riot';
    export default {
      components: { ChildComponent }
    }
  </script>
</parent-component>

Особенности работы с иерархией:

  • Дочерние компоненты полностью изолированы от состояния родителя, если специально не передать данные через свойства (props).
  • Использование slots позволяет вставлять произвольное содержимое внутрь дочернего компонента, сохраняя гибкость разметки.
  • События, возникающие в дочернем компоненте, могут быть переданы родителю через кастомные события.

Реактивность и состояние

Riot обеспечивает автоматическое обновление DOM при изменении состояния компонента. Любое свойство объекта, экспортированного через export default, становится реактивным:

<counter>
  <button oncl ick={ increment }>+</button>
  <span>{ count }</span>

  <script>
    export default {
      count: 0,
      increment() {
        this.count += 1;
      }
    }
  </script>
</counter>

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

  • Любые изменения в реактивных свойствах сразу отражаются в интерфейсе.
  • Методы компонента имеют доступ к this, который указывает на экземпляр компонента.
  • Реактивные свойства можно использовать для вычисляемых значений с помощью геттеров.

Управление состоянием приложения

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

// store.js
import { observable } from 'riot';

export const appStore = observable({
  user: null,
  setUser(user) {
    this.user = user;
  }
});

Использование store в компоненте:

<script>
  import { appStore } from './store';
  export default {
    onMounted() {
      this.user = appStore.user;
      appStore.on('update', () => this.update());
    }
  }
</script>

Особенности работы со store:

  • Любой компонент может подписываться на изменения глобального состояния.
  • Store обеспечивает единый источник правды, что упрощает синхронизацию данных между компонентами.
  • Поддержка событий позволяет реализовать реактивное взаимодействие без необходимости передавать данные через цепочку родитель-дочерний компонент.

Организация файлов и папок

Рекомендуемая структура проекта:

src/
 ├─ components/
 │   ├─ header.riot
 │   ├─ footer.riot
 │   └─ counter.riot
 ├─ store/
 │   └─ store.js
 ├─ pages/
 │   ├─ home.riot
 │   └─ about.riot
 └─ main.js

Принципы организации:

  • Компоненты и страницы разделяются для упрощения навигации и масштабирования.
  • Store и утилиты помещаются в отдельные папки, чтобы избежать смешения логики и представления.
  • Главный файл main.js импортирует корневой компонент и инициализирует приложение.

Жизненный цикл компонентов

Riot.js предоставляет методы жизненного цикла для управления состоянием и взаимодействием с DOM:

  • onBeforeMount() — вызывается перед рендером компонента.
  • onMounted() — после рендера, можно обращаться к DOM.
  • onBeforeUpdate() — перед обновлением реактивных свойств.
  • onUpdated() — после обновления DOM.
  • onBeforeUnmount() — перед удалением компонента.
  • onUnmounted() — после удаления, используется для очистки ресурсов.

Использование методов жизненного цикла позволяет оптимизировать производительность и корректно управлять ресурсами.

Работа с событиями

События в Riot.js делятся на локальные и глобальные. Локальные события обрабатываются через атрибуты, например onclick, oninput. Кастомные события создаются с помощью метода trigger:

this.trigger('customEvent', { data: 123 });

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

<child-component oncustomEv ent={ handleEvent }></child-component>

Такой подход обеспечивает слабую связанность компонентов и упрощает масштабирование приложения.

Итог структуры

Структура Riot.js приложения строится вокруг компонента как основной единицы. Четкое разделение на шаблон, логику и стили, поддержка реактивного состояния, централизованного store, жизненного цикла и событий обеспечивает гибкость, масштабируемость и простоту поддержки кода. Правильная организация файлов и папок позволяет поддерживать порядок даже в крупных приложениях.