Перейти к содержимому

Все статьи

NexusLite: небольшой фреймворк на TypeScript

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

Этот сайт использует NexusLite для создания страниц во время сборки. Браузер получает HTML, CSS и несколько скриптов для меню, выбора темы, фильтров и интерактивных обзоров проектов.

Что делает фреймворк

Основная реализация находится в framework/src/nexuslite.ts. Генерация статического сайта вынесена в отдельный модуль prerender.ts. Оба модуля входят в один пакет.

  • Функции элементов описывают дерево обычными вызовами функций.

  • createDOM() превращает дерево в элементы браузера.

  • createStore() хранит состояние и уведомляет подписчиков об изменениях.

  • createApp() связывает хранилище, корневой элемент и функцию отображения.

  • createRouter() поддерживает маршруты через hash и History API.

  • renderToString() создаёт HTML из тех же описаний элементов.

  • prerender() записывает таблицу маршрутов в статические файлы.

В пакете также есть HTTP-клиент, делегирование событий, перетаскивание и отложенное отображение списков. Приложение может использовать нужные ему части.

Небольшой интерактивный пример

Если в HTML есть элемент с id="app", этот код создаст счётчик:

import { createApp, createStore, div, h1, button, on } from 'nexuslite';

const store = createStore({ count: 0 });

createApp({
  root: '#app',
  state: store,
  render: (state) => div([
    h1(`Счётчик: ${state.count}`),
    button('Уменьшить', on('click', () => {
      store.setState({ count: state.count - 1 });
    })),
    button('Увеличить', on('click', () => {
      store.setState({ count: state.count + 1 });
    })),
  ]),
});

В NexusLite 1.2.0 функция createApp() очищает и заново строит корневой элемент при изменении состояния. Она не сравнивает старое и новое дерево через виртуальный DOM.

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

Создание статической страницы

Большей части портфолио не нужен реактивный корень. Содержимое можно создать один раз во время сборки:

import { readFileSync } from 'node:fs';
import { h } from 'nexuslite';
import { prerender } from 'nexuslite/prerender';

await prerender({
  routes: {
    '/': () => h('main', {}, [
      h('h1', {}, 'Мои проекты'),
      h('p', {}, 'Что я разработал и для чего это нужно.'),
    ]),
  },
  outDir: 'dist',
  template: readFileSync('index.template.html', 'utf8'),
  notFoundPath: false,
});

Шаблон представляет собой строку HTML с маркером <!--ROOT-->. NexusLite заменяет его содержимым страницы. Отдельная функция transform(html, path) позволяет задать заголовки и метаданные для каждого маршрута.

Сборка этого сайта находится в scripts/prerender.ts. Она создаёт языковые версии страниц, копирует ресурсы, формирует карту сайта и изображения для ссылок. Главная страница, каталог и подробные страницы используют один список проектов.

Что добавляет сам сайт

NexusLite работает с деревьями элементов и генерацией HTML. Сайт добавляет содержимое, дизайн-систему, метаданные и браузерные взаимодействия.

Master Design System хранит палитру, типографику, отступы и темы в одном файле токенов. Сборка создаёт из него CSS и использует ту же палитру для изображений ссылок. Шрифты загружаются с самого сайта.

Карточки проектов присутствуют в исходном HTML. Поиск и фильтры дополняют каталог после загрузки, а запрос сохраняется в адресе страницы. Содержимое остаётся доступным без JavaScript, а результатами поиска можно делиться.

Проверки и ограничения

В используемой здесь версии NexusLite 1.2.0 есть 167 тестов основных функций и генерации страниц. У сайта есть отдельные проверки маршрутов, метаданных, изображений, поиска и исправлений безопасности.

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

NexusLite подходит для обучения, небольших интерфейсов и сайтов со статическим содержимым. Большим приложениям могут понадобиться более сложное обновление DOM и широкая библиотека компонентов. Выбор стоит делать по требованиям задачи, а не только по размеру фреймворка.

Посмотреть код

Исходный код находится в github.com/AG064/nexuslite. Фреймворк распространяется по лицензии MIT. Начать знакомство можно с README и примеров.

Этот сайт показывает статический рендеринг в работе. Его HTML можно сопоставить с функциями в src/pages/, а на странице NexusLite есть интерактивный пример.