Разделение кода

SkateJS — это библиотека для создания веб-компонентов на JavaScript, основанная на стандартах Custom Elements и Shadow DOM. Она обеспечивает декларативное описание компонентов через объект Component, позволяя отделять структуру, поведение и стили. Основная цель — упрощение организации кода и поддержка масштабируемых приложений.

В SkateJS компоненты создаются с помощью функции defineComponent, которая принимает имя компонента и объект конфигурации. Ключевые свойства конфигурации включают props, render, events и lifecycle.

import { defineComponent, h } from 'skatejs';

const MyButton = defineComponent({
  tag: 'my-button',
  props: {
    disabled: Boolean
  },
  render(elem) {
    return <button disabled={elem.disabled}>Click me</button>;
  }
});

Разделение кода: структура и принципы

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

1. Разделение по слоям

  • Слой представления (View): отвечает только за отображение элементов и реакцию на пропсы. Использует JSX или функции рендеринга через h. Не содержит бизнес-логики.
  • Слой логики (Logic): управляет состоянием, обработкой событий, асинхронными операциями и взаимодействием с внешними сервисами.
  • Слой данных (Data/Model): определяет источники данных, модели, методы для работы с API или локальным хранилищем.

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

2. Props и State

Props в SkateJS определяют входные данные компонента и являются реактивными. Любое изменение пропса автоматически вызывает повторный рендер. Для локального состояния используют обычные поля класса или реактивные свойства через this.props.

import { defineComponent, h } from 'skatejs';

class Counter extends HTMLElement {
  static props = {
    count: Number
  };

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

  increment() {
    this.count++;
    this.render();
  }

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

customElements.define('my-counter', Counter);

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

SkateJS поддерживает стандартные методы жизненного цикла:

  • connectedCallback — выполняется при подключении элемента к DOM.
  • disconnectedCallback — при удалении компонента.
  • attributeChangedCallback — при изменении атрибутов.
  • render — используется для повторного обновления интерфейса при изменении состояния или пропсов.

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

4. Шаблоны и стили

SkateJS поддерживает Shadow DOM, что позволяет инкапсулировать стили и HTML. Для разделения шаблонов и логики удобно использовать отдельные файлы:

// counterTemplate.js
import { h } from 'skatejs';

export function counterTemplate(count, onClick) {
  return <button oncl ick={onClick}>Count: {count}</button>;
}
// Counter.js
import { defineComponent } from 'skatejs';
import { counterTemplate } from './counterTemplate.js';

const Counter = defineComponent({
  tag: 'my-counter',
  props: {
    count: Number
  },
  render(elem) {
    return counterTemplate(elem.count, () => elem.count++);
  }
});

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

5. Модули и именование

  • Каждый компонент располагается в отдельном файле с именем, совпадающим с тегом (my-button.js).
  • Служебные функции, шаблоны и константы выносятся в отдельные модули.
  • Общие утилиты объединяются в папки utils или services, чтобы не дублировать код.

6. Обработка событий

SkateJS позволяет привязывать обработчики через JSX или через события Custom Event. Рекомендуется отделять обработку события от интерфейса:

function handleClick(event) {
  console.log('Button clicked', event.target);
}

const MyButton = defineComponent({
  tag: 'my-button',
  render(elem) {
    return <button oncl ick={handleClick}>Click me</button>;
  }
});

При более сложной логике обработчик лучше вынести в отдельный модуль, чтобы его можно было тестировать независимо.

7. Тестирование

Четкое разделение кода облегчает написание unit-тестов:

  • Тесты для слоя логики не зависят от DOM.
  • Тесты для представления проверяют правильность рендеринга по данным.
  • Event-тесты изолируют взаимодействие компонентов через Custom Event.

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

  • Использовать именованные экспорты для всех компонентов и шаблонов.
  • Shadow DOM применять для компонентов, где требуется полная инкапсуляция стилей.
  • Для сложных компонентов создавать отдельные папки с файлом компонента, шаблоном, стилями и тестами.

Пример структуры:

/components
  /counter
    Counter.js
    counterTemplate.js
    counter.css
    Counter.test.js

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

Итоги структуры SkateJS-компонента

  • Props — входные данные и реактивность.
  • Render — чистый слой представления.
  • Logic — методы и обработчики событий, отделённые от шаблона.
  • Templates/Styles — отдельные модули для визуальной части.
  • Lifecycle — контроль подключения и удаления компонентов.
  • Events — обработка действий через функции и Custom Events.

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