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

Slim.js — это легковесная библиотека для построения компонентного интерфейса на JavaScript, ориентированная на минимализм и декларативное управление DOM. Правильная структура проекта критически важна для поддерживаемости и масштабируемости приложений, особенно при работе с большим количеством компонентов.

Структура каталогов

Рекомендуемая структура проекта строится вокруг компонентов и их логики:

/project-root
│
├─ /src
│  ├─ /components
│  │  ├─ header.slim.js
│  │  ├─ footer.slim.js
│  │  └─ card.slim.js
│  │
│  ├─ /views
│  │  ├─ home.slim.js
│  │  └─ about.slim.js
│  │
│  ├─ /services
│  │  └─ api.js
│  │
│  ├─ /store
│  │  └─ appState.js
│  │
│  └─ main.js
│
├─ /public
│  └─ index.html
│
├─ package.json
└─ webpack.config.js
  • components — здесь находятся отдельные UI-компоненты Slim.js, которые могут быть повторно использованы. Каждый компонент представлен отдельным файлом .slim.js.
  • views — страницы или контейнерные компоненты, объединяющие несколько мелких компонентов.
  • services — модуль для работы с API или другими внешними ресурсами.
  • store — глобальное состояние приложения, управляемое через Slim.js и кастомные подписки.
  • main.js — точка входа приложения, где происходит инициализация компонентов и их монтирование в DOM.

Организация компонентов

Каждый компонент Slim.js строится вокруг декларативной функции Slim.define:

Slim.define('my-card', class extends Slim {
  get template() {
    return `
      <div class="card">
        <h2>{{title}}</h2>
        <p>{{content}}</p>
      </div>
    `;
  }

  get is() {
    return 'my-card';
  }

  onBeforeCreated() {
    this.title = 'Заголовок';
    this.content = 'Описание карточки';
  }
});

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

  • get template() — возвращает HTML-шаблон компонента. Используются двойные фигурные скобки для биндинга свойств.
  • get is() — уникальное имя компонента для использования в DOM.
  • onBeforeCreated() — жизненный цикл компонента, позволяющий инициализировать свойства перед рендером.
  • Дополнительно можно использовать onCreated, onMounted, onUpdated, чтобы управлять поведением компонента на разных этапах жизненного цикла.

Взаимодействие компонентов

Slim.js поддерживает вложенные компоненты. Вызов дочернего компонента осуществляется простым включением его тега в шаблон родителя:

Slim.define('dashboard', class extends Slim {
  get template() {
    return `
      <div class="dashboard">
        <my-card title="Новости" content="Сегодня произошло..." />
        <my-card title="События" content="Запланировано..." />
      </div>
    `;
  }

  get is() { return 'dashboard'; }
});

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

Глобальное состояние удобно хранить в отдельном модуле /store/appState.js и подключать к компонентам через подписки:

export const appState = {
  user: null,
  notifications: [],
  subscribe(callback) {
    this._callbacks = this._callbacks || [];
    this._callbacks.push(callback);
  },
  notify() {
    if (this._callbacks) {
      this._callbacks.forEach(cb => cb(this));
    }
  }
};

Компонент может подписываться на изменения состояния в onCreated или onMounted:

Slim.define('user-info', class extends Slim {
  get template() {
    return `<div>{{userName}}</div>`;
  }

  get is() { return 'user-info'; }

  onMounted() {
    appState.subscribe(state => {
      this.userName = state.user ? state.user.name : 'Гость';
      this.update();
    });
  }
});

Подключение и инициализация

Главный файл main.js собирает компоненты и инициализирует их в DOM:

import './components/header.slim.js';
import './components/footer.slim.js';
import './views/home.slim.js';

document.addEventListener('DOMContentLoaded', () => {
  const app = document.querySelector('#app');
  app.innerHTML = `<home-page></home-page>`;
});

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

Рекомендации по масштабированию

  • Разделять компоненты на мелкие, атомарные единицы, чтобы их было легко тестировать и комбинировать.
  • Использовать сервисы для логики, не связанной напрямую с визуальной частью.
  • Для больших проектов создавать подкаталоги по типам компонентов (например, form, layout, widgets).
  • Следить за единой схемой именования тегов, чтобы избежать конфликтов в DOM.
  • Жизненный цикл компонентов использовать для синхронизации с глобальным состоянием и сторонними библиотеками, избегая прямых манипуляций с DOM извне.

Такое построение структуры обеспечивает читаемость, предсказуемость поведения и удобство поддержки при расширении функционала.