0% прочитано

Чому h(Link, ..., 'text') дає попередження Vue: вміст компонента треба передавати функцією

Інтерфейс може виглядати правильно, але Vue все одно попереджає про значення default slot. Причина проста: для компонента третій аргумент h() не варто передавати звичайним рядком. Показую різницю між h('a', ...) і h(Link, ...).

6 жовтня 2026 р. 6 хв читанняVue.js

Цей випадок був невеликим, але дуже показовим.

Посилання в інтерфейсі відображалося. Текст був на місці. Перехід працював.

Але в консолі Vue з’являлося попередження, пов’язане з default slot.

Причина виявилась не в самому Link і не в маршрутизації.

Проблема була в одному аргументі h(): для Vue-компонента текст передавався як звичайний рядок замість функції слота.

Якщо h() створює звичайний HTML-елемент, рядок може бути його дочірнім вмістом. Якщо h() створює Vue-компонент, вміст компонента має семантику slot.

Код виглядав цілком природно

Vue - Рядок переданий третім аргументом
return h(    Link,    {        href: item.url,    },    item.label,)

Якщо часто використовувати h() для звичайних елементів, такий запис виглядає логічно.

Є тип елемента, є властивості, є дочірній текст.

Але Link тут не є HTML-тегом. Це компонент.

І саме це змінює значення третього аргументу.

Для звичайного HTML-елемента рядок є нормальним вмістом

Vue - Звичайний HTML-елемент
return h(    'a',    {        href: item.url,    },    item.label,)

У випадку з 'a' Vue створює VNode звичайного DOM-елемента.

Третій аргумент може бути рядком, числом, іншим VNode або масивом дочірніх вузлів.

Тому звичка передавати текст третім аргументом сама по собі не є помилкою.

Важливо, що саме передано першим аргументом.

Для компонента дочірній вміст стає slot

Одна форма виклику, різна семантика
h(    'a',    props,    'Open',) 'a'-> HTML-елемент-> 'Open' є дочірнім текстом  h(    Link,    props,    'Open',)

Vue-компонент сам вирішує, де відобразити переданий йому дочірній вміст.

Для цього у Vue використовуються слоти.

Тому при ручному створенні VNode компонента через h() цей вміст передається як функція.

Виправлення виявилось дуже маленьким

Vue - Default slot як функція
return h(    Link,    {        href: item.url,    },    () => item.label,)

Сам текст не змінився.

Змінилася лише форма, у якій він передається компоненту.

() => item.label описує default slot і відповідає контракту компонента.

Після цього Vue вже не потрібно виправляти неправильну форму значення під час виконання.

Default slot можна передати і явним об’єктом

Vue - Явний default slot
return h(    Link,    {        href: item.url,    },    {        default: () => item.label,    },)

Для одного default slot коротка функція зручніша.

Але об’єкт добре показує саму модель: компонент отримує не просто children, а набір slot-функцій.

Ця форма також природна, коли компонент має кілька іменованих слотів.

Функція тут не просто синтаксична вимога

Функція дозволяє дочірньому компоненту викликати slot тоді, коли йому потрібно його відобразити.

Це також допомагає Vue точніше відстежувати реактивні залежності саме там, де slot використовується.

Тому попередження не варто сприймати як косметичну претензію до стилю коду.

Воно вказує, що компонент отримав вміст не в тій формі, для якої розрахований механізм slot.

Те, що інтерфейс працює, не робить попередження неважливим

Саме це зробило помилку цікавою.

Не було білого екрана, exception або зламаного переходу.

Vue зміг відобразити значення, тому проблема легко виглядала як "нешкідливе попередження".

Але консоль у таких випадках показує невідповідність контракту раніше, ніж вона перетвориться на складніший баг.

Я волію виправити першопричину, а не просто залишити warning, якщо результат візуально правильний.

Першим аргументом h() я перевіряю, що саме створюється

Швидка перевірка
Є Vue warning біля h()? 1. Подивитися на перший аргумент. 'a''div''span'-> звичайний елемент  LinkButtonCustomComponent-> Vue-компонент  

Особливо легко пропустити проблему в таблицях і фабриках колонок

Vue - Компонент усередині динамічного renderer
{    key: 'name',     cell: ({ row }) => h(        Link,        {            href: row.url,        },        () => row.name,    ),}

У шаблоні Vue slot зазвичай виглядає природно і про нього майже не потрібно думати.

Ручний h() частіше з’являється в описах колонок, динамічних таблицях, фабриках компонентів або інших програмних renderer.

Саме там легко механічно використати форму, яка була правильною для 'span' або 'a', але вже неправильна для компонента.

Я не переписую всі h() після одного такого warning

Після такого виправлення легко зробити протилежну помилку і почати загортати у функції будь-який третій аргумент h().

Це не потрібно.

Для звичайних елементів рядок залишається нормальним дочірнім вмістом.

Я змінюю саме ті місця, де VNode створюється для компонента і третій аргумент фактично є slot.

Що я перевіряю при подібному Vue warning

Мій короткий список:

  • Що передано першим аргументом h(): HTML-тег чи компонент?
  • Що передано третім аргументом?
  • Якщо це компонент, чи є його вміст функцією slot?
  • Чи потрібен лише default slot, чи кілька іменованих?
  • Чи не повторюється неправильний виклик у кількох динамічних renderer?
  • Чи зникло попередження після виправлення, а поведінка залишилась тією самою?

Однаковий h() не означає однакові children

У цьому багу не було складної реактивності або великої архітектурної проблеми.

Потрібно було лише правильно прочитати контракт h().

Для звичайного елемента третій аргумент може бути простим дочірнім текстом.

Для компонента той самий візуальний вміст уже належить до slot і передається функцією.

Маленька різниця в синтаксисі відображає важливу різницю в тому, що саме Vue створює: DOM-елемент чи компонент.

Коли h() поводиться дивно, я спочатку дивлюся не на текст, а на те, чи перший аргумент є елементом або компонентом.