Динамическая регистрация компонентов во Vue
Если вы используете однофайловые компоненты Vue, то вам, конечно же, знаком способ подключения компонента внутри другого компонента:
- импортируем компонент;
- регистрируем его в объекте компонентов родителя;
- используем компонент в шаблоне
<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>
Ничего необычного. Мы импортируем два компонента, регистрируем их, и показываем один из них в зависимости от значения входного параметра.
Вы наверняка постоянно так делаете. Но то же самое можно сделать немного лучше.
Способ 2: используем <component />
Component это встроенное во Vue определение компонента. Он работает как плейсхолдер для отображения любого компонента, имя которого передаётся через параметр :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>
Здесь мы используем <component />
и создали вычисляемое свойство с именем необходимого компонента, что позволило отказаться от громоздкой конструкции v-if/v-else
.
Но это ещё не всё. Несмотря на использование <component />
, нам по прежнему приходится импортировать и регистрировать компоненты UserInfo и CompanyInfo. Вот если бы можно было импортировать только необходимый компонент... И тут нам поможет динамический импорт
Способ 3: динамический импорт + <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>
В этом варианте для импорта компонента мы используем функцию, которая возвращает Promise. И если всё идёт хорошо, то Promise разрешается и загружается только необходимый нам компонент, который мы передаём <component />
для рендеринга. Да, так можно. Согласно документации, свойство :is
может содержать:
- Имя компонента или
- Объект компонента
А объект компонента это именно то, что нам нужно.
И вот мы существенно сократили наш код, нам больше не нужно вручную импортировать и регистрировать компоненты. Всё происходит динамически. Более подробную информацию о динамическом импорте смотрите в документации.
Таким образом мы немного упростили себе жизнь, сократив количество кода, и мимоходом уменьшили объём итогового файла приложения. Ведь динамический импорт он на то и динамический, что необходимый компонент будет подгружаться только при необходимости.