Цей випадок був невеликим, але дуже показовим.
Посилання в інтерфейсі відображалося. Текст був на місці. Перехід працював.
Але в консолі Vue з’являлося попередження, пов’язане з default slot.
Причина виявилась не в самому Link і не в маршрутизації.
Проблема була в одному аргументі h(): для Vue-компонента текст передавався як звичайний рядок замість функції слота.
Код виглядав цілком природно
return h( Link, { href: item.url, }, item.label,)Якщо часто використовувати h() для звичайних елементів, такий запис виглядає логічно.
Є тип елемента, є властивості, є дочірній текст.
Але Link тут не є HTML-тегом. Це компонент.
І саме це змінює значення третього аргументу.
Для звичайного 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() цей вміст передається як функція.
Виправлення виявилось дуже маленьким
return h( Link, { href: item.url, }, () => item.label,)Сам текст не змінився.
Змінилася лише форма, у якій він передається компоненту.
() => item.label описує 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-компонент Особливо легко пропустити проблему в таблицях і фабриках колонок
{ 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() поводиться дивно, я спочатку дивлюся не на текст, а на те, чи перший аргумент є елементом або компонентом.



