Шаблонизация

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


Использование render

Метод render отвечает за генерацию HTML-контента компонента на основе текущего состояния. Он вызывается автоматически при изменении свойств или состояния компонента.

import { define, props, Component, h } from 'skatejs';

class MyComponent extends Component {
  static props = {
    name: props.string
  };

  renderCallback() {
    return (
      <div>
        <p>Hello, {this.name}!</p>
      </div>
    );
  }
}

define('my-component', MyComponent);

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

  • renderCallback() — метод, который возвращает виртуальный DOM компонента.
  • h (hyperscript) — функция, создающая виртуальные узлы, если не используется JSX.
  • Изменение свойства name автоматически инициирует повторный рендер компонента.

JSX и гиперскрипт (hyperscript)

SkateJS позволяет использовать два подхода к описанию DOM внутри компонента:

  1. JSX-подход Требует настройки сборщика (например, Babel) для преобразования JSX в вызовы h. Этот способ делает код более читаемым и декларативным:

    renderCallback() {
      return (
        <ul>
          {this.items.map(item => <li>{item}</li>)}
        </ul>
      );
    }
  2. Hyperscript-подход Используется напрямую функция h, что удобно для проектов без сборщика:

    renderCallback() {
      return h('ul', null, this.items.map(item => h('li', null, item)));
    }

Оба способа эквивалентны по функционалу, разница только в синтаксисе.


Реактивные свойства и их связь с шаблоном

SkateJS обеспечивает реактивность через определение свойств компонента с помощью props. Любое изменение свойства автоматически вызывает повторный рендер компонента.

class Counter extends Component {
  static props = {
    count: props.number
  };

  constructor() {
    super();
    this.count = 0;
  }

  increment() {
    this.count += 1;
  }

  renderCallback() {
    return <button oncl ick={() => this.increment()}>Count: {this.count}</button>;
  }
}

define('my-counter', Counter);

Особенности:

  • Любое изменение свойства count вызывает повторное выполнение renderCallback().
  • Связь между состоянием и DOM полностью декларативная, что исключает необходимость ручного обновления элементов.

Условная отрисовка

Шаблонизация в SkateJS позволяет использовать условные конструкции, управляя видимостью элементов или выбором шаблонов.

renderCallback() {
  return (
    <div>
      {this.isLoggedIn ? (
        <p>Добро пожаловать, {this.userName}!</p>
      ) : (
        <p>Пожалуйста, войдите в систему.</p>
      )}
    </div>
  );
}

Особенности:

  • Любая логика отображения реализуется прямо внутри шаблона.
  • Можно комбинировать с тернарными операторами, функциями и массивными операциями (map, filter).

Итерация по массивам данных

Для отображения списков и таблиц используется стандартная итерация через map:

renderCallback() {
  return (
    <ul>
      {this.items.map(item => (
        <li key={item.id}>{item.label}</li>
      ))}
    </ul>
  );
}

Примечания:

  • Атрибут key рекомендуется указывать для элементов, создаваемых в цикле, чтобы оптимизировать рендеринг.
  • Изменение массива элементов автоматически пересчитает DOM без ручного вмешательства.

Слоты и композиция шаблонов

SkateJS поддерживает слоты, позволяя вставлять произвольный контент внутрь компонента:

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

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

  • Контент, переданный в слот, рендерится в момент вставки компонента.
  • Можно использовать именованные слоты для нескольких областей.

Работа с динамическими классами и стилями

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

renderCallback() {
  return (
    <div class={{ active: this.isActive, disabled: !this.isEnabled }}>
      Контент компонента
    </div>
  );
}
  • Классы с true добавляются к элементу, false — игнорируются.
  • Для inline-стилей можно передавать объект: <div style={{ color: this.color, fontSize: '16px' }}>.

Шаблонизация вложенных компонентов

Компоненты SkateJS можно вкладывать друг в друга, создавая иерархию UI:

class ItemList extends Component {
  static props = { items: props.array };

  renderCallback() {
    return (
      <ul>
        {this.items.map(item => <Item key={item.id} data={item} />)}
      </ul>
    );
  }
}

class Item extends Component {
  static props = { data: props.object };

  renderCallback() {
    return <li>{this.data.name}</li>;
  }
}

Особенности:

  • Каждый компонент управляет своим собственным рендером.
  • Изменения в дочернем компоненте не влияют на родителя, если не изменяются общие свойства.

SkateJS предоставляет мощную и гибкую систему шаблонизации, сочетающую реактивность, JSX-подобный синтаксис и гиперскрипт. Использование условной отрисовки, итерации, слотов и динамических классов позволяет создавать сложные интерфейсы без прямой манипуляции DOM. Эта архитектура упрощает поддержку и тестирование компонентов, делая их максимально декларативными и предсказуемыми.