Atomico — это современная библиотека для создания веб-компонентов с использованием JavaScript. Она строится вокруг идеи декларативного описания компонентов и управления состоянием с реактивностью, аналогичной React, но без виртуального DOM, что обеспечивает более лёгкое и быстрое взаимодействие с браузером.
Компонент в Atomico определяется через функцию
component, принимающую объект с конфигурацией:
import { c, component, html, useState } from "atomico";
function Counter() {
const [count, setCount] = useState(0);
return html`
<div>
<p>Счёт: ${count}</p>
<button oncl ick=${() => setCount(count + 1)}>Увеличить</button>
</div>
`;
}
customElements.define("my-counter", component(Counter));
Ключевые моменты:
html — это шаблонная функция для описания разметки
компонента.useState — реактивный хук для хранения состояния.component — функция, создающая полноценный
веб-компонент из функционального компонента.Atomico позволяет легко управлять свойствами компонента через пропсы.
Пропсы определяются с использованием props внутри объекта
конфигурации или через деструктуризацию аргументов функции:
function Greeting({ name }) {
return html`<h1>Привет, ${name}!</h1>`;
}
Greeting.props = {
name: String
};
customElements.define("my-greeting", component(Greeting));
Изменение пропсов автоматически обновляет компонент. Можно комбинировать пропсы с локальным состоянием:
function Timer({ start }) {
const [time, setTime] = useState(start);
setInterval(() => setTime(time + 1), 1000);
return html`<div>Время: ${time}</div>`;
}
Timer.props = { start: Number };
customElements.define("my-timer", component(Timer));
Atomico включает набор реактивных хуков, схожих с React:
useState(initial) — локальное состояние.useEffect(callback, deps) — эффекты с отслеживанием
зависимостей.useMemo(callback, deps) — мемоизация вычислений.useRef(initial) — хранение ссылки на DOM-элемент или
данные между рендерами.useEvent(callback) — обработчики событий с сохранением
ссылок.Пример использования useEffect:
function Logger({ message }) {
useEffect(() => {
console.log("Сообщение изменилось:", message);
}, [message]);
return html`<div>${message}</div>`;
}
Logger.props = { message: String };
customElements.define("my-logger", component(Logger));
Atomico поддерживает scoped-стили для компонентов с помощью CSS-in-JS
через шаблонные строки css:
import { c, component, html, css } from "atomico";
const style = css`
div {
color: blue;
font-weight: bold;
}
`;
function StyledBox() {
return html`<div class=${style}>Это синий текст</div>`;
}
customElements.define("my-styled-box", component(StyledBox));
Стили автоматически изолируются на уровне веб-компонента, что предотвращает конфликты с глобальным CSS.
Atomico предоставляет механизм для создания и обработки событий:
function Clicker() {
const handleClick = () => {
dispatch("clicked", { detail: "Кнопка нажата" });
};
return html`<button oncl ick=${handleClick}>Нажми меня</button>`;
}
Clicker.props = {};
customElements.define("my-clicker", component(Clicker));
dispatch позволяет генерировать кастомные события,
которые могут быть пойманы родительскими элементами с использованием
стандартного API addEventListener.Atomico поддерживает динамическую загрузку компонентов, что снижает размер основного бандла:
import { c, component, html, useState } from "atomico";
function LazyWrapper() {
const [Component, setComponent] = useState(null);
async function load() {
const module = await import("./LazyComponent.js");
setComponent(() => module.default);
}
return html`
<div>
<button oncl ick=${load}>Загрузить компонент</button>
${Component ? html`<${Component} />` : ""}
</div>
`;
}
customElements.define("my-lazy-wrapper", component(LazyWrapper));
Rollup является идеальным инструментом для сборки библиотек на
Atomico благодаря его tree-shaking, поддержке ES-модулей и возможности
создавать несколько форматов (ESM, UMD, CJS). Типичная конфигурация
rollup.config.js для Atomico выглядит так:
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import babel from "@rollup/plugin-babel";
import { terser } from "rollup-plugin-terser";
export default {
input: "src/index.js",
output: [
{
file: "dist/bundle.esm.js",
format: "esm",
sourcemap: true
},
{
file: "dist/bundle.umd.js",
format: "umd",
name: "MyLibrary",
sourcemap: true
}
],
plugins: [
resolve(),
commonjs(),
babel({ babelHelpers: "bundled" }),
terser()
]
};
@rollup/plugin-node-resolve обеспечивает поиск модулей
в node_modules.@rollup/plugin-commonjs конвертирует CommonJS модули в
ES.@rollup/plugin-babel транспилирует современный JS.terser минимизирует итоговый бандл.Такой подход позволяет распространять Atomico-библиотеки в виде готовых модулей, легко интегрируемых в любые проекты.
Atomico поддерживает интеграцию с любыми сторонними библиотеками через обычный импорт. Компоненты можно оборачивать, используя хуки или прокидывая пропсы:
import { c, component, html } from "atomico";
import { format } from "date-fns";
function DateFormatter({ date }) {
return html`<div>${format(date, "dd.MM.yyyy")}</div>`;
}
DateFormatter.props = { date: Date };
customElements.define("my-date-formatter", component(DateFormatter));
Реактивность сохраняется даже при использовании внешних функций, если значения корректно передаются через пропсы или состояния.
Atomico поддерживает вложенные компоненты и композицию:
function Card({ title, content }) {
return html`
<div class="card">
<h2>${title}</h2>
<p>${content}</p>
</div>
`;
}
Card.props = { title: String, content: String };
function CardList() {
return html`
<div>
<${Card} title="Первый" content="Содержимое 1" />
<${Card} title="Второй" content="Содержимое 2" />
</div>
`;
}
customElements.define("my-card-list", component(CardList));
Использование вложенных компонентов позволяет строить сложные интерфейсы без потери реактивности и с сохранением полной изоляции стилей.
Эта структура обеспечивает удобную разработку библиотек на Atomico, гибкое управление состоянием, эффективную сборку с Rollup и лёгкую интеграцию с существующими экосистемами JavaScript.