Попробуй на секунду отвлечься от шума технологического хайпа и взглянуть на современный фронтенд со стороны. Чтобы вывести на экран обычную форму с тремя полями ввода и кнопкой, среднестатистический проект подтягивает мегабайты зависимостей: React или Vue, компилятор JSX/TSX, тяжелый бандлер со сложными плагинами трансформации, библиотеки управления состоянием и изощренные правила инвалидации мемоизации (useMemo, useCallback).
В погоне за абстракциями мы незаметно забыли, что браузер — это невероятно быстрая и мощная среда, которая умеет манипулировать узлами DOM напрямую. Как мы разбирали в статье Рендеринг больших списков в вебе: системное сравнение CSS content-visibility и виртуализации DOM, накладные расходы промежуточных виртуальных деревьев нередко становятся главным узким местом производительности клиентских приложений.
Разработчик под ником pain64 задался простым инженерным вопросом: можно ли построить удобную компонентную модель без Virtual DOM, компиляторов и тяжелых рантаймов? Результатом стал проект LandTS — крошечная библиотека на чистом TypeScript, чье ядро занимает ровно 177 строк кода.
Три кита LandTS: компоненты, зоны и стили
Архитектура библиотеки решает три базовых вопроса фронтенда предельно лаконично:
- Создание компонентов через функцию
l: Никакого синтаксического сахара JSX. Функцияl(tag, init)создает стандартный DOM-узел и передает его в лямбду инициализации. Если нужно вложить один элемент в другой, ты просто передаешь родительский узел первым аргументом:l(parent, tag, init). - Зонный рендеринг через функцию
lz: Вместо того чтобы сравнивать гигантские деревья Virtual DOM или внедрять сложные графы реактивных сигналов, LandTS вводит понятие «зон» (land zones). Зона оборачивает блок разметки, зависящий от динамического состояния. При вызове методаzone.rerender()очищается и пересоздается только конкретная зона, а не всё приложение. - Изолированные стили через теговый шаблон
css: Стили описываются прямо рядом с компонентом и монтируются к элементу методом.applyTo(element).
Архитектура асинхронных компонентов
Если компонент выполняет сетевой запрос, достаточно сделать лямбду асинхронной — LandTS автоматически отобразит плейсхолдер до загрузки данных:
// Асинхронная инициализация с получением данных
const userProfile = l('section', async (root) => {
const res = await fetch('/api/user/me');
const user = await res.json();
l(root, 'h2', (title) => {
title.innerText = `Привет, ${user.name}!`;
});
});
Практический пример: интерактивный счетчик
Давай посмотрим, как на LandTS выглядит полноценный компонент счетчика с изолированной зоной перерисовки и стилями:
import { css, l, lz } from './land.ts';
const counterApp = l('div', (root) => {
let count = 0;
// Монтируем стили прямо к корневому узлу
css`
padding: 24px;
max-width: 320px;
font-family: system-ui, sans-serif;
border: 1px solid #dcdcdc;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
`.applyTo(root);
// Выделяем реактивную зону для блока значения
lz(root, (zone) => {
l(zone, 'h3', (h) => {
h.innerText = `Текущий счет: ${count}`;
h.style.color = count > 5 ? '#e11d48' : '#1e293b';
});
l(zone, 'button', (btn) => {
btn.innerText = 'Увеличить на 1';
btn.style.padding = '8px 16px';
btn.style.cursor = 'pointer';
btn.onclick = () => {
count += 1;
// Запускаем точечный ререндер только этой зоны
zone.rerender();
};
});
});
});
document.body.appendChild(counterApp);
Если функция инициализации элемента возвращает Promise (асинхронный компонент), LandTS автоматически подставляет временный плейсхолдер до завершения сетевого запроса, делая работу с удаленными данными прозрачной.
Конечно, не стоит пытаться переписывать гигантские Enterprise-порталы на 177 строк кода: в библиотеке нет готового каталога сложных UI-компонентов. Но для создания легковесных браузерных расширений, встроенных виджетов обратной связи, микрофронтендов и админ-панелей, где критичен каждый килобайт бандла, LandTS становится глотком свежего воздуха.
