Rollup для библиотек

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

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

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.