Или чистый 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, чтобы обновить компонент после вызова обработчика события, что эквивалентно предыдущему примеру.

Жизненный цикл

Последний аспект, который нам нужно понять перед тем, как приступить к разработке реальных приложений, — это жизненный цикл компонента.

Он состоит всего из четырех этапов:

  1. Создать компонент

  2. Присоединить к DOM

  3. Обновить DOM

  4. Отсоединить от 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)


  1. Worst_su
    22.08.2024 22:15
    +3

    Вот так и хочется воскликнуть, узрев очередной фреймворк


    1. aleksandy
      22.08.2024 22:15

      Или так
      Троллейбус из буханки хлеба
      Троллейбус из буханки хлеба


    1. isumix Автор
      22.08.2024 22:15

      Так не фреймворк жеж, а 1 функция если через JSX ))


  1. 19Zb84
    22.08.2024 22:15

    Эти упрощенные фреймворки все больше похожи на веб компоненты из ствндарта html. Почему сразу со стандапта не начать ?


    1. gmtd
      22.08.2024 22:15
      +1

      Веб-компонентам кое-чего не хватает для комфортной разработки на них.

      Байндинга с шаблоном, директив в шаблоне, реактивных переменных, не строковых пропсов. Добавляем это и получаем Vue


      1. 19Zb84
        22.08.2024 22:15
        +1

        То что вы перечислили, если разбирать, то не осоо и нужные вещи и что они удобные, это прямо сказать на любителя и не факт что будет правильно работать.

        Что может быть проще этого ?

        const data = component.querySelector('container')
        data.addEventListener('click', () => { data.textContent = 'test'})
        data.removeEventListener('click', () => { data.textContent = 'test'})


        1. gmtd
          22.08.2024 22:15

          Для приложений уровня разобранных в этой статье это подходит, да
          Для чего-то более серьезного - не очень

          Ну и я забыл роутинг клиентский


          1. 19Zb84
            22.08.2024 22:15

            Ну и я забыл роутинг клиентский

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

            https://developer.mozilla.org/en-US/docs/Web/API/Navigation/navigate_event

            Для чего-то более серьезного - не очень

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


            1. isumix Автор
              22.08.2024 22:15

              В наше время библиотеки и обертки для роутинга уже не так актуальны. Любой роутинг делается в 2 строчки кода.


              1. KwI
                22.08.2024 22:15

                Как это?


        1. isumix Автор
          22.08.2024 22:15

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


        1. Gary_Ihar
          22.08.2024 22:15

          Ваш пример чутка нерабочий, необходимо расширить до 4 строк кода


      1. alexnozer
        22.08.2024 22:15

        Байндинга с шаблоном, директив в шаблоне, реактивных переменных, не строковых пропсов. Добавляем это и получаем Vue

        Или Lit. Он, всё же, by design разработан для создания веб-компонентов и приложений на их базе. Vue всё же был создан несколько для другого, что не отменяет возможности скомпилировать Vue-компонент в веб-компонент.


      1. isumix Автор
        22.08.2024 22:15

        Еще со времен Ангуляра, не понимал в чем фишка директив в шалонах. У нас уже есть один язык программирования ЯваСкрипт, зачем еще один для шаблонов, лишняя ментальная нагрузка.


        1. alexnozer
          22.08.2024 22:15

          На мой взгляд причина та же, почему когда-то в PHP, Java и других языках ушли от шаблонов с вставками кода к шаблонизаторам. Язык даёт слишком большую гибкость и это не всегда нужно.

          Об этом говорил создатель Vue в одном из своих докладов.


    1. isumix Автор
      22.08.2024 22:15

      Фьюзор хорошо подходит для создания и обновления ДОМ внутри стандартных вэб компонентов. Он кстати использует их частично для определения коннекта/дисконнекта к ДОМ.


  1. stvoid
    22.08.2024 22:15

    Так вроде есть Lit уже, может даже не он один.
    Я конечно не против, пусть выживет сильнейший, но в чем фича?


    1. 19Zb84
      22.08.2024 22:15
      +1

      Их очень много сейчас. А как сильнейшего выбрать можно, если каждый день лучший в мире фреймворк выходит ? Есть какая нибудь методика ?


      1. aleksandy
        22.08.2024 22:15
        +6

        Методика проста: фреймворк должен быть лишён фатального недостатка.


        1. 19Zb84
          22.08.2024 22:15

          А какие фатальные недостатки есть ?

          На первый взгляд могу только сказать что это очистка инициализация данных

          Нечеткий/разбросанный по файлам процес преобразований

          Взаимодействие любой части кода с любой частью кода.

          Но самые лучшие фреймовки в мире как правило ограничиваются написание hello world для описания ( как и эта статья ) уверяя, что больше вам ничего не понадобится, а потом ты годами борешься с тем чего не хватает и ждешь новой версии с нетерпением, а потом с раздражением


          1. meonsou
            22.08.2024 22:15
            +1

            1. 19Zb84
              22.08.2024 22:15

              Ну да )) Я как раз стараюсь понять эти моменты что бы лишить "фатального недостатка" супер мега фреймворки со своей стороны. Если это "хорошо, оно должно быть хоошо. "


        1. isumix Автор
          22.08.2024 22:15

          Это наше - всё ))


    1. isumix Автор
      22.08.2024 22:15

      Фьюзор лишь про создание и обновление ДОМ. Ничего другого в нем нет. Но тем не менее я хотел показать что даже с этим набором можно делать все вещи что и с фрэймворками. Тоесть текущие библиотеки переусложнены ИМХО.


  1. gmtd
    22.08.2024 22:15

    Ещё один вы внебрачный сын Реакта


    1. isumix Автор
      22.08.2024 22:15

      Абсолютно! )) Будучи профессиональным разработчиком на Реакт, с него родимого черпаю вдохновение ))


  1. francyfox
    22.08.2024 22:15
    +4

    -- Какого моё предназначение мастер?
    -- Стать новой реактивной библиотекой
    -- Я ведь буду обладать какими-то уникальными фичами и стану лучше чем предшественники?
    -- Ммм... Ты мало весишь


    1. isumix Автор
      22.08.2024 22:15

      Одна функция АПИ если создавать через JSX, простота, гибкость и прозрачность, такого у других нет ))


  1. DarthVictor
    22.08.2024 22:15

    Что-то вроде lit и stenciljs?


    1. isumix Автор
      22.08.2024 22:15

      В них много всего происходит за кадром. Классы менее гибки чем функции. Декораторы. Что-то как-то там работает, не не видно как, много скрытой логики.

      В Фьюзере все на поверхности. Определили функцию с данными, создали компонент, обновили, всё. Данные это обычные переменные.