Блок: устройство
Блок описывается файлом .nu.ts. Внутри — регистрация шаблонов через синтаксис nu (block()(...)). Рендеринг состоит из двух проходов: все режимы — и структурные, и отображения в HTML — в первом (renderToBemJson), второй (renderToHtml) только сериализует результат. Подробно о режимах — в разделе шаблоны nu.
Структура файла
Один файл blocks/<name>/<name>.nu.ts описывает один блок:
import { block } from 'nuckty';
interface ButtonProps {
type?: 'button' | 'submit';
size?: 's' | 'm' | 'l';
}
export default function templates(): void {
// Регистрация блока: движок загружает .nu.ts по зависимостям и вызывает templates
block<ButtonProps>('button')({
tag: 'button',
attrs: (n) => ({ type: n.props?.type ?? 'button' }),
mods: (n) => ({ size: n.props?.size ?? 'm' }),
});
// Элементы и модификаторы — отдельными декларациями
block('button').elem('icon')({ tag: 'span' });
}
Файл экспортирует default function templates() — точку входа. Движок вызывает её при загрузке блока. Шаблон регистрируют через block(...) — он привязан к коллекции шаблонов текущей сборки.
Режимы
Весь блок описан в одном файле. Все режимы — и структурные, и режимы отображения — движок применяет на одном проходе над деревом (renderToBemJson); второй проход (renderToHtml) только сериализует результат.
Структурные режимы меняют дерево:
// content: задать содержимое
block<{ summary?: string }>('card').elem('body')({
content: (n) => n.props?.summary ?? '',
});
// replace: заменить блок
block<{ color?: string }>('badge').replace((n) => ({
block: 'tag', mods: { color: n.props?.color ?? 'default' },
}));
// wrap: обернуть
block('root').wrap((n) => ({
tag: 'html',
content: [{ tag: 'head' }, { tag: 'body', content: n }],
}));
Режимы отображения задают, каким узел выйдет в HTML:
// tag, attrs, cls, mods, elemMods, mix, js, bem
block('menu')({ tag: 'ul' });
block('menu').elem('item')({ tag: 'li' });
block<{ url?: string; id?: string }>('link')({
attrs: (n) => ({ href: n.props?.url ?? '/', 'data-track': n.props?.id ?? '' }),
});
Минимизация разметки — не режим. На втором проходе движок минимизирует готовую разметку. Подробнее — HTML на выходе.
Сопутствующие ассеты
Рядом с .nu.ts могут лежать CSS и JS блока:
blocks/button/
button.nu.ts
button.css
button.js
CSS и JS включаются по записи блока в декларации бандла. Использование блока в контенте само по себе не добавляет его ассеты. Подробнее — зависимости блоков.
Специфичность и переопределение
Декларации в одной коллекции ранжируются по специфичности: block < elem < mod < elemMod < when. При равной специфичности выбирается последняя. Например, внутри одной функции регистрации можно дополнить ранее заданные модификаторы:
// после базовой декларации в той же функции templates():
block('button')({ mods: (_n, next) => ({ ...next(), size: 'l' }) }); // всегда 'l'
При переопределении через уровни загружается только последний файл .nu.ts блока. Чтобы сохранить базовые декларации, явно импортируйте и вызовите их функцию регистрации внутри своего templates(), прежде чем добавить собственные. Иначе новый файл должен описывать всё необходимое поведение.
Чего в файле нет
- Нет ручной регистрации каждого блока. Движок находит
.nu.tsпо зависимостям, загружает файл и вызываетtemplates. Подробнее — зависимости. - Нет импорта CSS и JS внутри
.nu.ts. Ассеты лежат рядом, и бандлер находит и добавляет их в сборку по зависимостям. - Нет вызова
applyNext(). Вместо отдельного метода — параметрnextв функции режима:(n, next) => ....