velund
Version:
Ядро согласованного рендеринга
230 lines (181 loc) • 18.3 kB
Markdown
**Velund** — это не просто плагин для Vite, а **интегрированная экосистема** для создания кросс-платформенных UI-компонентов. Он позволяет фронтенд-разработчикам создавать компоненты с использованием привычных шаблонизаторов, а Velund компилирует их в готовые к использованию библиотеки, которые могут быть интегрированы и рендерены на любом бэкенде (Node.js, Python, PHP) через четко определенные API.
Velund фокусируется на:
- **Контрактной разработке**: Четкие схемы данных между фронтендом и бэкендом.
- **Модульности**: Возможность использовать готовые или создавать собственные рендереры и генераторы.
- **Изоляции**: UI-компоненты независимы от конкретной логики бэкенда.
- **Автоматизации**: Генерация библиотек и DTO для бэкенда.
- **Агностик к шаблонизатору**: Поддержка Twig (`@zebrains/velund-twig`), Jinja/Nunjucks (`@zebrains/velund-jinja`), HTML (`@velund/html`). Система позволяет легко добавлять новые шаблонизаторы.
- **Типизированные компоненты**: Строгое определение `props` и `context` компонентов с помощью схем TypeBox (транслируются в JSON Schema).
- **Backend-интеграция**: Автоматическая генерация специализированных библиотек для бэкенд-языков (Node.js, Python, PHP), которые предоставляют готовый `Renderer` класс для рендеринга компонентов.
- **Горячая перезагрузка (HMR)**: Мгновенные обновления UI-компонентов в процессе разработки.
- **Управление ассетами**: Автоматическое управление путями к статическим ассетам, готовое к употреблению бэкендом.
- **Асинхронные данные**: Поддержка асинхронной подготовки данных (`prepare` метод) для компонентов.
- **Расширяемая архитектура**: Возможность использования готовых или создания собственных рендереров и генераторов благодаря API, предоставляемому `@velund/core`.
Используйте CLI-инструмент `create-velund` для быстрой инициализации проекта:
```bash
pnpm create velund
npm create velund@latest
yarn create velund
```
`create-velund` интерактивно спросит вас о названии проекта, предпочитаемом шаблонизаторе и генераторе для бэкенда, и автоматически настроит `vite.config.ts` и установит необходимые зависимости.
Установите `velund` и необходимые рендереры/генераторы:
```bash
pnpm add velund @zebrains/velund-twig
pnpm add @velund/node
```
```typescript
// vite.config.ts
import { defineConfig } from 'vite';
import velund from 'velund'; // Основной плагин Velund
import twigRenderer from '@zebrains/velund-twig'; // Пример: рендерер для Twig
import phpGenerator from '@zebrains/velund-php'; // Пример: генератор для PHP
export default defineConfig({
plugins: [
velund({
// -- Основной шаблонизатор и генератор для проекта --
renderer: 'twig', // Указывает основной шаблонизатор (Twig, Jinja, HTML)
generator: 'php', // Указывает основной генератор для бэкенда (Node.js, Python, PHP)
// -- Регистрация рендереров --
// Все используемые рендереры должны быть зарегистрированы здесь.
// @velund/html уже встроен.
renderers: [
twigRenderer(), // Экземпляр Twig-рендерера
],
// -- Регистрация генераторов --
// Все используемые генераторы должны быть зарегистрированы здесь.
// @velund/node уже встроен.
generators: [
phpGenerator(), // Экземпляр Python-генератора
],
// -- Опции для интеграции с бэкендом --
assetsUrl: '/assets', // URL-путь по которому бэкенд должен отдавать статические ассеты Velund.
// Velund ожидает, что бэкенд будет обслуживать dist/assets по этому пути.
renderUrl: '/__render', // URL-путь для API-эндпоинта, который бэкенд должен предоставить.
// Используется в dev-режиме для рендеринга компонентов в рантайме.
// -- Прочие опции --
strictTemplateExtensions: false, // `true` если нужно строго проверять расширения файлов шаблонов (например, только .twig)
// nodeConfig?: VelundNodeGeneratorOptions; // Конфигурация для `@velund/node` генератора.
}),
],
});
```
Velund-компоненты определяются в виде TypeScript-файлов (обычно `.vel.ts`) и являются вашим основным способом создания UI. Вы можете автоматически собирать их через виртуальный модуль `virtual:velund/components`
```typescript
// src/components/ProductCard.vel.ts
import { defineComponent } from 'velund/common'; // Хелпер для определения компонента
import { Type } from '@sinclair/typebox'; // Используется для описания схем данных
import template from './ProductCard.twig'; // Путь к файлу шаблона
export default defineComponent({
name: 'ProductCard', // Уникальное имя компонента
template, // Путь к связанному файлу шаблона (например, `.twig`, `.j2`, `.html`)
// -- Схема входных свойств (PropTypes) --
// `propsSchema` определяет данные, которые компонент принимает извне.
// Бэкенд-библиотека будет использовать эту схему для валидации входных данных.
propsSchema: Type.Object({
id: Type.Number(), // ID продукта
}),
// -- Схема контекста шаблона (ContextType) --
// `contextSchema` определяет структуру данных, которые будут доступны внутри шаблона.
// Это может быть объединение входных props и данных, полученных в `prepare` методе.
contextSchema: Type.Object({
title: Type.String(), // Название продукта
price: Type.Number(), // Цена продукта
description: Type.Optional(Type.String()), // Описание (необязательно)
}),
// -- Метод подготовки данных (Runtime Data Fetching) --
// `prepare` - асинхронная функция, которая вызывается для получения или подготовки данных для компонента.
// Она принимает `props` в соответствии с `propsSchema` и должна возвращать объект,
// который будет объединен с `props` и передан в шаблон в качестве `context`.
async prepare(props) {
// Пример: получить данные о продукте из API
const res = await fetch(`/api/products/${props.id}`); // Используется `fetch` (доступен в Node.js и браузере)
const productData = await res.json();
return productData; // Эти данные будут доступны в шаблоне как часть `context`
},
});
```
Velund **не просто генерирует JSON-схемы**, он создает **полноценные библиотеки для вашего бэкенд-языка**.
1. **Генерация библиотеки**: При сборке вашего фронтенд-проекта (`npm run build`), выбранный `generator` (например, `@velund/node`) генерирует библиотеку в вашей `build.outDir` (например, `dist/`) директории.
2. **`Renderer` класс**: Эта сгенерированная библиотека предоставляет основной класс `Renderer` (или аналогичный) и специализированные классы/функции для каждого вашего компонента (например, `ProductCard`).
3. **API для бэкенда**: Бэкенд-разработчик использует этот `Renderer` класс для:
- Инициализации (обычно требуется указать путь к скомпилированным шаблонам).
- Регистрации `prepare` методов: Если компонент использует асинхронный `prepare` метод (как в примере выше), бэкенд должен будет **предоставить синхронную или асинхронную реализацию этого метода** в своей среде. Сгенерированная типизация или описание для IDE поможет бэкенду понять, какие аргументы (`props`) должен принимать этот метод и какой контекст возвращать.
- Рендеринга компонента: Вызов метода `render(componentName, context, meta?: boolean)` предоставит готовый HTML.
После сборки фронтенда, вы можете использовать сгенерированную библиотеку в вашем Node.js-проекте:
```javascript
import express from 'express';
import path from 'path';
import {
HomePageComponent,
ProductPageComponent,
Renderer,
} from '../velund/lib';
const app = express();
const port = 3333;
// Предоставление директории с ассетами
const publicDir = path.join(process.cwd(), 'velund/assets');
app.use('/assets', express.static(publicDir));
// Регистрация prepare-метода дла компонента HomePage
HomePageComponent.registerPrepare(async () => {
const products = await (
await fetch('https://fakestoreapi.com/products')
).json();
products[0].title = Date.now() + products[0].title;
return {
products,
};
});
// Регистрация prepare-метода для компонента ProductPage
ProductPageComponent.registerPrepare(async ({ id }) => {
const product = await (
await fetch('https://fakestoreapi.com/products/' + id)
).json();
return product;
});
// Инициализация рендер-класса
const renderer = new Renderer();
// Регистрация роутов
app.get('/', async (req, res) => {
const html = await renderer.render('HomePage', {});
res.status(200).send(html).end();
});
app.get('/ProductPage', async (req, res) => {
const r = await renderer.render('ProductPage', req.query);
res.status(200).send(r).end();
});
app.listen(port, () => {
console.log(`🚀 Server running at http://localhost:${port}`);
});
```
- **Сервировка ассетов**: Бэкенд должен быть настроен на обслуживание статических файлов из директории Velund `dist/assets` по пути, указанному в `assetsUrl` (`/assets` по умолчанию).
- **Runtime-рендеринг**: Бэкенд может реализовать эндпоинт по пути `renderUrl` (`/__render` по умолчанию) для динамического рендеринга компонентов в рантайме, используя логику, подобную той, что предоставляют генераторы. Это полезно для SSR или API-запросов.
Интерфейс `iTwigPluginConfig` (определен в `src/types.ts`) для функции `velund()`:
| Опция | Тип | По умолчанию | Описание |
| -------------------------- | ----------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `renderer` | `string` | `'html'` | **Обязательный**. ID основного рендерера для проекта (например, `'twig'`, `'jinja'`, `'html'`). Используется для выбора дефолтного рендерера. |
| `generator` | `string` | `'node'` | **Обязательный**. ID основного генератора для проекта (например, `'node'`, `'python'`, `'php'`). Указывает, какую бэкенд-библиотеку Velund должен генерировать. |
| `assetsUrl` | `string` | `'/assets'` | **Путь URL**, по которому бэкенд должен будет **обслуживать статические ассеты** (JS,CSS, изображения), сгенерированные Vite. Это критически важный контракт для бэкенда. |
| `renderUrl` | `string` | `'/__render'` | **Путь URL** для API-эндпоинта, который бэкенд _должен_ будет предоставить для **динамического рендеринга компонентов в рантайме**. Velund использует этот же URL в режиме разработки для собственного отладочного сервера. |
| `strictTemplateExtensions` | `boolean` | `false` | Если `true`, Velund будет строго проверять, что расширения файлов шаблонов соответствуют зарегистрированным для рендереров (например, только `.twig` для Twig-рендерера). |
| `generators` | `VelundGeneratorDescriptor[]` | `[]` | Массив **экземпляров** генераторов, которые Velund должен использовать (например, `[pythonGenerator(), phpGenerator()]`). **`@velund/node` встроен по умолчанию.** |
| `renderers` | `VelundRendererDescriptor[]` | `[]` | Массив **экземпляров** рендереров, которые Velund должен использовать (например, `[twigRenderer(), jinjaRenderer()]`). **`@velund/html` встроен по умолчанию.** |
| `nodeConfig` | `VelundNodeGeneratorOptions` | `{}` | Конфигурация, специфичная для `@velund/node` генератора (если используется). |
MIT © DNTZ