Продуктивна реєстрація компонентів у Vue

5 хв. читання

Якщо ви мали справу з Однофайловими компонентами, ви, скоріше за все, знаєте як «викликати» компонент з іншого компоненту:

  1. Робимо import дочірнього компонента;
  2. Реєструємо його в об'єкті components батьківського компонента;
  3. Додаємо компонент до 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 — класна річ, тому ознайомтеся з нею, якщо ви ще цього не зробили, і ваші застосунки стануть кращими.

Порівняємо три рішення, які ми обговорювали, на практиці:

CodeSandbox

До речі, ви можете навіть налаштувати назву bundle-файлу та спосіб завантаження для динамічних імпортів з магічними коментарями.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 6.2K
Приєднався: 7 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація