SkateJS — это современная библиотека для создания Web Components на основе стандартов Custom Elements, предоставляющая мощный и гибкий механизм для управления жизненным циклом компонентов через хуки. Хуки в SkateJS представляют собой функции, которые вызываются на определённых этапах жизненного цикла элемента, позволяя добавлять логику без необходимости напрямую вмешиваться в классы компонентов.
Ключевые особенности хуков в SkateJS:
Жизненный цикл SkateJS-компонента включает несколько этапов:
Создание элемента (constructor и createdCallback) На этом этапе происходит инициализация состояния и базовой структуры элемента. Хуки здесь позволяют задать начальные значения, установить подписки или подготовить внутренние свойства.
Подключение элемента к DOM (connectedCallback) Вызывается, когда элемент добавляется в DOM. Используется для активации наблюдателей, запуска анимаций, установки слушателей событий и асинхронной загрузки данных.
Отключение элемента от DOM (disconnectedCallback) Хуки на этом этапе позволяют корректно очищать ресурсы: удалять обработчики событий, отменять таймеры, отписываться от подписок.
Обновление (updatedCallback) Вызывается после изменений свойств или атрибутов компонента. Предоставляет возможность оптимизировать перерисовку или реагировать на изменение данных.
SkateJS предоставляет стандартный набор хуков, которые можно использовать напрямую в определении компонента:
useStateuseState управляет локальным состоянием компонента.
Состояние является реактивным: изменения автоматически инициируют
обновление DOM.
import { define, useState, html } from 'skatejs';
define('counter-element', {
render: ({ state }) => html``,
setup({ state }) {
state.count = 0;
},
connected({ state, element }) {
element.addEventListener('click', () => state.count++);
}
});
Особенности useState:
setup.useEffectuseEffect выполняет побочные эффекты после рендера
компонента. Аналог useEffect в React, но с учетом
особенностей Web Components.
useEffect(() => {
const interval = setInterval(() => console.log('tick'), 1000);
return () => clearInterval(interval);
}, []); // пустой массив — эффект выполняется один раз при подключении
Особенности useEffect:
cleanup),
которая выполняется при отключении компонента.useRefuseRef позволяет создавать ссылку на DOM-элемент или
любую переменную, которая сохраняется между рендерами:
const buttonRef = useRef();
useMemo и
useCallbackuseMemo оптимизирует вычисления, которые зависят от
определённых значений:
const computedValue = useMemo(() => expensiveComputation(state.count), [state.count]);
useCallback позволяет мемоизировать функции, чтобы их
ссылки оставались стабильными между рендерами:
const handleClick = useCallback(() => state.count++, [state.count]);
SkateJS позволяет комбинировать несколько хуков в одном компоненте, обеспечивая модульность и переиспользуемость кода. Пример сочетания состояния, эффекта и мемоизации:
define('timer-element', {
render: ({ state }) => html`${state.seconds}`,
setup({ state }) {
state.seconds = 0;
},
connected({ state }) {
useEffect(() => {
const interval = setInterval(() => state.seconds++, 1000);
return () => clearInterval(interval);
}, []);
}
});
Рекомендации по композиции:
SkateJS поддерживает создание кастомных хуков, которые инкапсулируют повторяющуюся логику. Пример: хук для управления локальным таймером:
function useTimer(initial = 0) {
const state = useState({ count: initial });
useEffect(() => {
const interval = setInterval(() => state.count++, 1000);
return () => clearInterval(interval);
}, []);
return state;
}
Такой хук можно использовать в нескольких компонентах:
define('countdown-element', {
render: ({ timer }) => html`${timer.count}`,
connected({ element }) {
const timer = useTimer(10);
element.appendChild(timer.count);
}
});
Порядок вызова хуков имеет значение. SkateJS вызывает их строго в том порядке, в котором они объявлены в компоненте. Нарушение последовательности может привести к некорректной работе состояний и эффектов. Правила:
useState и useRef.useMemo и useCallback.useEffect для работы с побочными
эффектами.SkateJS позволяет связывать хуки с реактивными свойствами через
объект props:
define('input-element', {
props: {
value: String
},
connected({ props }) {
useEffect(() => {
console.log('Value changed:', props.value);
}, [props.value]);
}
});
props автоматически вызывает эффекты с
соответствующими зависимостями.