Шаблони Vue компілюються в рендер-функції - функції, що повертають віртуальні вузли (VNode). Ці функції можна писати й вручну.
import { h, ref } from 'vue';
export default {
props: { level: { type: Number, default: 2 } },
setup(props, { slots }) {
return () => h(`h${props.level}`, { class: 'heading' }, slots.default?.());
},
};
h(тег або компонент, props/атрибути, діти). З плагіном @vitejs/plugin-vue-jsx те саме можна писати як JSX:
setup(props, { slots }) {
const Tag = `h${props.level}`;
return () => <Tag class="heading">{slots.default?.()}</Tag>;
}
Коли рендер-функції справді потрібні:
- дуже динамічна структура, яку незручно виражати директивами: тег обирається програмно, дерево будується з конфігурації (конструктор форм за JSON-схемою, рендер Markdown-AST у компоненти);
- компоненти-«обгортки», що маніпулюють слотами: переставити, обгорнути кожен дочірній вузол, вставити роздільники між елементами списку;
- функціональні компоненти без стану - проста функція
(props, { slots }) => h(...); - бібліотеки компонентів, де потрібен повний контроль над VNode.
Чому для звичайних компонентів кращі шаблони:
- оптимізації компілятора. Компілятор шаблонів знає, які частини статичні, а які динамічні: піднімає статичні вузли, позначає динамічні прапорцями (patch flags), будує «блоки» з плоским списком динамічних вузлів. Під час оновлення Vue пропускає все статичне. Рукописна рендер-функція цих підказок не має - порівнюється все дерево;
- читабельність для команди й дизайнерів, близькість до HTML;
- інструменти: підсвітка, перевірка типів у шаблонах (Volar), форматування.
Пастки рендер-функцій:
- VNode не можна використовувати двічі в одному дереві - для повторення треба створювати нові;
v-model,v-if,v-forу JSX - це звичайний JavaScript (тернарні оператори,map), аv-modelпотребує ручногоmodelValue+onUpdate:modelValue;- слоти передаються об'єктом функцій:
h(Comp, null, { default: () => ..., header: () => ... }).
Практичне правило: шаблони - за замовчуванням, рендер-функції - точково там, де шаблон стає незручним.