Якщо ви мали справу з Однофайловими компонентами, ви, скоріше за все, знаєте як «викликати» компонент з іншого компоненту:
- Робимо
import
дочірнього компонента; - Реєструємо його в об'єкті
components
батьківського компонента; - Додаємо компонент до
template
або функціїrender
.
<template>
<some-random-thing />
</template>
<script>
import SomeRandomThing from './components/SomeRandomThing'
export default {
components: {
SomeRandomThing,
},
}
</script>
Це загальна послідовність дій, яка згодом швидко набридає. У статті оглянемо шаблон (або навіть два), щоб уникнути таких повторень. Як бонус, покращимо продуктивність нашого застосунку.
Почнемо!
Уявіть компонент Header
з інформацією та розміткою для заголовка нашого застосунку. Тепер уявіть, що ця інформація може бути пов'язана з користувачем або компанією та залежати від значення певного параметра.
Нехай існують компоненти UserInfo
та CompanyInfo
. Ми хочемо рендерити той чи інший, в залежності від значення параметра, яке ми сформували раніше.
Версія №1: старий добрий спосіб
Та сама послідовність, яку ми описували на початку.
Більшість вважає, що це спосіб «за замовчуванням»:
<template>
<div>
<company-info v-if="isCompany" />
<user-info v-else />
...
</div>
</template>
<script>
import UserInfo from './components/UserInfo'
import CompanyInfo from './components/CompanyInfo'
export default {
components: {
UserInfo,
CompanyInfo,
},
props: {
isCompany: { type: Boolean, default: false },
},
}
</script>
Нічого особливого. Ми імпортуємо два компоненти, реєструємо їх і відображаємо в залежності від значення prop.
Напевно, ви завжди використовували такий «шаблон». Він непоганий, але ми можемо краще.
Версія №2: на допомогу приходить <component />
У Vue вже вбудовано Component. <component />
організовує місце для іншого компонента та приймає спеціальний prop :is
з його назвою.
<template>
<div>
<component :is="componentName" />
</div>
</template>
<script>
import UserInfo from './components/UserInfo'
import CompanyInfo from './components/CompanyInfo'
export default {
components: {
UserInfo,
CompanyInfo,
},
props: {
isCompany: { type: Boolean, default: false },
},
computed: {
componentName () {
return this.isCompany ? 'company-info' : 'user-info'
},
},
}
</script>
Зверніть увагу як ми створюємо обчислюване значення з назвою потрібного компонента. Таким чином ми прибираємо зайву v-if/v-else
логіку на користь всемогутнього <component />
. Ми навіть можемо передати деякі props, як зазвичай. Хіба не чудово?
Але тут є проблемний момент. Нам треба імпортувати та реєструвати всі допустимі значення для prop :is
. Тут ми імпортуємо та реєструємо UserInfo
та CompanyInfo
.
Якби ж щось дозволило нам імпортувати всі ці компоненти «на льоту», нам не потрібно було б реєструвати їх...
Така концепція вже в дії!
Версія №3: динамічний імпорт + <component />
(бонус: розділення коду)
Поглянемо як динамічний імпорт та <component />
взаємодіють на практиці:
<template>
<div>
<component :is="componentInstance" />
</div>
</template>
<script>
export default {
props: {
isCompany: { type: Boolean, default: false },
},
computed: {
componentInstance () {
const name = this.isCompany ? 'CompanyInfo' : 'UserInfo'
return () => import(`./components/${name}`)
}
}
}
</script>
Тут імпорт перетворюється на функцію, що повертає проміс. Вона завантажує потрібний модуль у рантаймі, якщо проміс вирішиться (тобто нічого не зламається і не відхилиться).
Тож що відбувається? Ми досі використовуємо нашого нового друга <component />
, але тут маємо справу не з простим рядком, а з цілим об'єктом компонента.
Як зазначено у документації, prop :is
може містити:
- Назву зареєстрованого компонента
- Об'єкт параметрів компонента
Останній пункт — саме те, що нам потрібно!
Зверніть увагу, як ми уникали імпорту та реєстрації компонентів, оскільки динамічний import
виконується у рантаймі.
Більше інформації про Vue та динамічні імпорти в офіційній документації.
Примітка
Помітьте, що ми отримуємо доступ до нашого prop this.isCompany
поза виразом з динамічним імпортом. Це обов'язково, адже в іншому разі Vue не зможе застосувати свою реактивність та оновити значення нашого компонента при зміні prop. Спробуйте самостійно.
Оскільки у прикладі ми отримали доступ до prop поза динамічним імпортом (створили змінну name
), Vue знає, що наша обчислювана властивість componentInstance
«залежить» від this.isCompany
.
Застереження
При використанні динамічних імпортів, на етапі збірки Webpack створить chunk-файл для кожного файлу, що відповідає виразу всередині функції import
.
Наш приклад не дуже реальний, але уявіть, що у теці /components
800 компонентів. Тоді Webpack створить 800 chunk-файлів.
Це не те, що ми хотіли. Тому переконайтеся, що ви створили суворі вирази та/або дотримуєтесь угоди щодо тек. Можна згрупувати компоненти, які ви хочете розділити, у теках /components/chunks
або /components/bundles
. Так ви будете знати які компоненти розділяє Webpack.
Наостанок, ознайомтеся з лаконічним шаблоном, який ми згадували на початку.
Наші «умовні» компоненти тепер розділені кодом.
Якщо для такого компоненту ви виконаєте команду npm run build
, ви помітите, що Webpack створить спеціальний bundle-файл для UserInfo.vue
та окремий — для CompanyInfo.vue
. Він робить так за замовчуванням.
Це чудова фіча, оскільки наші користувачі не будуть завантажувати згадані файли, поки застосунок не зробить запит. Тобто зменшиться початковий розмір bundle-файлу та покращиться продуктивність всього застосунку.
Code Splitting — класна річ, тому ознайомтеся з нею, якщо ви ще цього не зробили, і ваші застосунки стануть кращими.
Порівняємо три рішення, які ми обговорювали, на практиці:
До речі, ви можете навіть налаштувати назву bundle-файлу та спосіб завантаження для динамічних імпортів з магічними коментарями.
Ще немає коментарів