Или чистый JavaScript с двумя вспомогательными функциями?
В этой статье я расскажу о том, как разрабатывать веб-компоненты с использованием библиотеки Fusor и преимуществах данного подхода.
Такие компоненты можно будет затем собирать в полноценные веб-приложения, сопоставимые с теми, что созданы с использованием React, Angular, Vue, Solid, Svelte и т.д.
АПИ Fusor состоит всего из двух основных функций:
Создать DOM-элемент, обернутый в специальный объект.
Обновить DOM-элемент, обернутый в специальный объект.
Плюс еще несколько редко используемых функций, таких как:
Получить DOM-элемент из специального объекта.
Вам не обязательно что-либо знать об этом специальном объекте.
Создание DOM-элемента
Создание через JSX
import { getElement } from "@fusorjs/dom";
const count = 0;
// Создание через JSX
const message = <div>Seconds {count} elapsed</div>;
document.body.append(getElement(message)); // Получить
Мы использовали функции АПИ создать и получить.
Альтернативное создание без JSX
import { div } from "@fusorjs/dom/html";
const message = div("Seconds ", count, " elapsed"); // Создать
Обновление элемента DOM
import { getElement, update } from "@fusorjs/dom";
let count = 0;
const message = <div>Seconds {() => count} elapsed</div>; // Создать
document.body.append(getElement(message)); // Получить
setInterval(() => {
count += 1;
update(message); // Обновить
}, 1000);
Мы использовали функцию АПИ обновить. Она обновляет элемент DOM и все его дочерние элементы рекурсивно. Она получает новые значения из вызовов функций, делая их динамическими.
Дочерние элементы, атрибуты и свойства могут быть динамическими.
<div class={() => (toggle ? "on" : "off")} />
Обновления DOM будут происходить только в том случае, если новые значения отличаются от текущих.
Установка параметров
В основном устанавка параметров происходит как обычно:
<div style="padding:1em" />
Однако иногда вам потребуется различать атрибуты и свойства. Чтобы указать их тип, вы можете добавить суффиксы _a
или _p
к их названиям:
<div name1_a="attribute" name2_p="property" />
Чтобы добавить обработчик события, вы всегда должны использовать суффикс _e
:
<div click_e={() => "event handler"} />
Есть и дополнительные типы, и некоторые могут принимать опции для обеспечения полной совместимости со стандартами W3C:
<div click_e_capture_once={() => "event handler"} />
Создание компонента
Создавайте свои компоненты, используя специальные объекты Fusor. Инкапсулируйте состояние и параметры внутри функций. Используйте заглавные буквы для имен ваших компонентов.
Вот пример компонента кнопки счётчика:
import { getElement, update } from "@fusorjs/dom";
const CountingButton = (props) => {
let count = props.count ?? 0; // Состояние
const self = (
<button
click_e={() => {
count += 1;
update(self);
}}
>
Clicked {() => count} times
</button>
);
return self;
};
const App = () => (
<div style="padding:1em">
<p>Three counting buttons</p>
<CountingButton />
<CountingButton count={22} />
<CountingButton count={333} />
</div>
);
document.body.append(getElement(App()));
Компонент CountingButton
обновляет только малую часть своего DOM-элемента, не затрагивая остального приложения.
Когда вы разберетесь, как работает этот компонент, то посмотрите как можно переписать его немного короче, с тем же результатом:
const CountingButton = ({ count = 0 }) => (
<button
click_e={(event, self) => {
count += 1;
update(self);
}}
>
Clicked {() => count} times
</button>
);
Каждая функция обработчика событий получает два аргумента: стандартный объект события и текущий специальный объект.
Теперь, если вы разобрались и с этим примером, посмотрите и на кратчайшую версию того же компонента:
const CountingButton = ({ count = 0 }) => (
<button click_e_update={() => (count += 1)}>
Clicked {() => count} times
</button>
);
Мы добавили опцию update
, чтобы обновить компонент после вызова обработчика события, что эквивалентно предыдущему примеру.
Жизненный цикл
Последний аспект, который нам нужно понять перед тем, как приступить к разработке реальных приложений, — это жизненный цикл компонента.
Он состоит всего из четырех этапов:
Создать компонент
Присоединить к DOM
Обновить DOM
Отсоединить от DOM
import { getElement, update } from "@fusorjs/dom";
const IntervalCounter = ({ count = 0 }) => {
console.log("1. Создать компонент");
return (
<div
mount={(self) => {
console.log("2. Присоединить к DOM");
const timerId = setInterval(() => {
count++;
update(self);
console.log("3. Обновить DOM");
}, 1000);
return () => {
clearInterval(timerId);
console.log("4. Отсоединить от DOM");
};
}}
>
Since mounted {() => count} seconds elapsed
</div>
);
};
const instance = <IntervalCounter />;
const element = getElement(instance);
document.body.append(element);
setTimeout(() => element.remove(), 15000);
Свойство mount
содержит функцию, которая выполняется, когда компонент добавляется в DOM. Эта функция принимает один аргумент: текущий специальный объект. Она также может вернуть другую функцию, которая выполняется, когда компонент удаляется из DOM.
Мы полностью контролируем эти четыре этапа жизненного цикла. Это позволяет нам создавать, обновлять и сравнивать компоненты, используя кастомные асинхронные или параллельные стратегии для разных компонентов, с учетом кадров анимации.
Это конец туториала
Как вы могли заметить из этого туториала, Fusor прост, лаконичен и ясен. Чаще всего вам будет достаточно использовать только две функции АПИ. Тем не менее, он также предлагает множество возможностей для кастомизации и гибкости, когда это необходимо.
Итак, чтобы ответить на вопрос в заголовке, Fusor — это маленькая библиотека JavaScript, а не фреймворк, но с помощью нее можно достичь тех же результатов, что и с фреймворками.
С чего начать
Все примеры выше доступны на CodeSandbox.
Также ознакомьтесь с примером SVG аналоговых часов.
Вот пример реального приложения.
Стартовые шаблоны проектов:
Спасибо
Комментарии (30)
19Zb84
22.08.2024 22:15Эти упрощенные фреймворки все больше похожи на веб компоненты из ствндарта html. Почему сразу со стандапта не начать ?
gmtd
22.08.2024 22:15+1Веб-компонентам кое-чего не хватает для комфортной разработки на них.
Байндинга с шаблоном, директив в шаблоне, реактивных переменных, не строковых пропсов. Добавляем это и получаем Vue
19Zb84
22.08.2024 22:15+1То что вы перечислили, если разбирать, то не осоо и нужные вещи и что они удобные, это прямо сказать на любителя и не факт что будет правильно работать.
Что может быть проще этого ?const data = component.querySelector('container') data.addEventListener('click', () => { data.textContent = 'test'}) data.removeEventListener('click', () => { data.textContent = 'test'})
gmtd
22.08.2024 22:15Для приложений уровня разобранных в этой статье это подходит, да
Для чего-то более серьезного - не очень
Ну и я забыл роутинг клиентский19Zb84
22.08.2024 22:15Ну и я забыл роутинг клиентский
Пожалуйста. Роутинг клиентский, при чем он качественнее всего того, что я видел раньше, потому что в примере из документации явно видно как сделать все стадии обработки загрузки страницы.
https://developer.mozilla.org/en-US/docs/Web/API/Navigation/navigate_event
Для чего-то более серьезного - не очень
Это неправда. Ограничений по сложности нет, если правильно архитектуру собрать.
isumix Автор
22.08.2024 22:15Это императивный подход, для больших приложений лучше использовать декларативный, как в этой статье.
alexnozer
22.08.2024 22:15Байндинга с шаблоном, директив в шаблоне, реактивных переменных, не строковых пропсов. Добавляем это и получаем Vue
Или Lit. Он, всё же, by design разработан для создания веб-компонентов и приложений на их базе. Vue всё же был создан несколько для другого, что не отменяет возможности скомпилировать Vue-компонент в веб-компонент.
isumix Автор
22.08.2024 22:15Еще со времен Ангуляра, не понимал в чем фишка директив в шалонах. У нас уже есть один язык программирования ЯваСкрипт, зачем еще один для шаблонов, лишняя ментальная нагрузка.
isumix Автор
22.08.2024 22:15Фьюзор хорошо подходит для создания и обновления ДОМ внутри стандартных вэб компонентов. Он кстати использует их частично для определения коннекта/дисконнекта к ДОМ.
stvoid
22.08.2024 22:15Так вроде есть Lit уже, может даже не он один.
Я конечно не против, пусть выживет сильнейший, но в чем фича?19Zb84
22.08.2024 22:15+1Их очень много сейчас. А как сильнейшего выбрать можно, если каждый день лучший в мире фреймворк выходит ? Есть какая нибудь методика ?
aleksandy
22.08.2024 22:15+7Методика проста: фреймворк должен быть лишён фатального недостатка.
19Zb84
22.08.2024 22:15А какие фатальные недостатки есть ?
На первый взгляд могу только сказать что это очистка инициализация данных
Нечеткий/разбросанный по файлам процес преобразований
Взаимодействие любой части кода с любой частью кода.
Но самые лучшие фреймовки в мире как правило ограничиваются написание hello world для описания ( как и эта статья ) уверяя, что больше вам ничего не понадобится, а потом ты годами борешься с тем чего не хватает и ждешь новой версии с нетерпением, а потом с раздражением
isumix Автор
22.08.2024 22:15Фьюзор лишь про создание и обновление ДОМ. Ничего другого в нем нет. Но тем не менее я хотел показать что даже с этим набором можно делать все вещи что и с фрэймворками. Тоесть текущие библиотеки переусложнены ИМХО.
francyfox
22.08.2024 22:15+4-- Какого моё предназначение мастер?
-- Стать новой реактивной библиотекой
-- Я ведь буду обладать какими-то уникальными фичами и стану лучше чем предшественники?
-- Ммм... Ты мало весишьisumix Автор
22.08.2024 22:15Одна функция АПИ если создавать через JSX, простота, гибкость и прозрачность, такого у других нет ))
DarthVictor
22.08.2024 22:15Что-то вроде lit и stenciljs?
isumix Автор
22.08.2024 22:15В них много всего происходит за кадром. Классы менее гибки чем функции. Декораторы. Что-то как-то там работает, не не видно как, много скрытой логики.
В Фьюзере все на поверхности. Определили функцию с данными, создали компонент, обновили, всё. Данные это обычные переменные.
Worst_su
Вот так и хочется воскликнуть, узрев очередной фреймворк
aleksandy
Или так
isumix Автор
Так не фреймворк жеж, а 1 функция если через JSX ))