Функциональные компоненты Vue.js: что, почему и когда?
Перевод статьи: Austin Gil Vue.js functional components: what, why, and when?
tldr: если ваш компонент не нуждается в отслеживании состояния, превращение его в функциональные компоненты может повысить производительность рендеринга на 70%.
Что такое функциональный компонент?
Функциональные компоненты (не путать с функциями рендеринга Vue) — это компонент, который не содержит ни состояния, ни экземпляра.
Проще говоря это означает, что компонент не поддерживает реактивность и не может сделать ссылку на себя через ключевое слово this.
Использование через шаблон
<template functional> <div>...</div> </template>
Использование через код компонента
<script> export default { functional: true, ... render(h): { ... } } </script>
Доступ к свойствам компонента
Если у компонента нет состояния или экземпляра вы можете задаться вопросом, как можно ссылаться на такие вещи, как данные или методы. К счастью, Vue предоставляет параметр «context» для базовой функции рендеринга.
«context» является объектом со следующими свойствами:
- props: Объект предоставляющий свойства props
- children: Массив дочерних элементов VNode
- slots: Функция, возвращающая объект slots
- scopedSlots: (v2.6.0+) Объект, который предоставляет переданные scoped слоты. Также устанавливает нормальные слоты как функции.
- data: Весь объект данных, переданный компоненту как второй аргумент createElement
- parent: Ссылка на родительский компонент
- listeners: (v2.3.0+) Объект, содержащий зарегистрированных родителей event listeners. Это псевдоним data.on
- injections: (v2.3.0+) При использовании параметра inject он будет содержать разрешенные инъекции.
Доступ к параметру context довольно прост. Например, если мы хотим что-то сделать с props, это может выглядеть так:
<template functional> <div> {{props.someProp}} </template> <script> export default { props: { someProp: String } } </script>
<script> export default { functional: true, props: { someProps: String }, render(h, ctx): { const someProps = ctx.props.someProp ... } } </script>
Краткое примечание о функциональных компонентах и props:
В функциональном компоненте вам фактически не нужно регистрировать props, которые он принимает. Однако, если вы зарегистрируете их, они все равно будут проверены на соответствие их конфигурации. Лично я считаю, что по-прежнему рекомендуется зарегистрировать их, чтобы можно было бы указать тип переменной, required, значение по умолчанию и/или пользовательские валидаторы.
Зачем использовать функциональные компоненты?
Функциональные компоненты могут сделать доступ к вашим компонентам немного сложнее в работе или создать некоторую сложность, так зачем же их использовать?
Скорость.
Поскольку функциональные компоненты не имеют состояния, они не требуют дополнительной инициализации для таких вещей, как система реактивности Vue.
Функциональные компоненты будут по-прежнему реагировать на изменения входных данных, такие как новые props, но внутри самого компонента нет способа узнать, когда его данные изменились, поскольку он не поддерживает свое собственное состояние.
Для тех из вас, кто предпочитает факты, я провел тестирование для отображения 1000 элементов списка как компонентов с состоянием по сравнению с функциональными компонентами, и в то время как компоненты с состоянием занимали в среднем 140 мс, функциональные компоненты занимали около 40 мс.
Проверти сами:
https://codesandbox.io/s/vue-template-yterr?fontsize=14
Для большого приложения вы увидите значительные улучшения событий рендеринга/обновления DOM после реализации функциональных компонентов.
Когда использовать функциональные компоненты?
Функциональные компоненты, вероятно, не подходят во многих случаях. В конце концов, смысл использования JavaScript-фреймворка заключается в создании приложений, которые более реактивны. В Vue вы не можете сделать это без системы реактивности.
Однако есть несколько отличных вариантов использования функциональных компонентов:
- Простые компоненты, без внутренней логики. Например, кнопки, pills, теги, карточки, даже полные страницы со статичным текстом, типа страница «About».
- «Компоненты более высокого порядка», которые используется для создания разметки или основных функций вокруг другого компонента.
- Каждый раз, когда вы попадаете в цикл (v-for), элементы цикла обычно так же являются подходящими кандидатами.
Недостатки
Есть одна небольшая проблема, с которой я столкнулся для довольно конкретного случая использования. При использовании тега <template> и при приеме данных через props иногда требуется изменить данные внутри шаблона.
Со стандартным компонентом Vue это легко сделать, используя методы или computed props. С функциональными компонентами у нас нет доступа к методам или computed props.
Тем не менее, есть способ сделать это.
Допустим, наш компонент принимает prop «user», который является объектом со свойствами «firstName» и «lastName», и мы хотим отобразить шаблон, который показывает полное имя пользователя.
В функциональном компоненте <template> мы можем сделать это, создав новый метод прямо в определении нашего компонента, а затем использовать свойство $options, предоставляемое Vue, для доступа к нашему специальному методу:
<template functional> <div>{{ $options.userFullName(props.user)}}</div> </template> <script> export default { props: { user: Object }, userFullName(user) { return `${user.firstName} ${user.lastName}` } } </script>
Я слышал об еще одной проблеме, я лично с ней не сталкивался, но один из участников сообщества обнаружил, что при попытки использовать вложенные функциональные компоненты, которые имеют scoped slots, поведение отличается от таких же компонентов но с использованием внутреннего состояния https://github.com/vuejs/vue-loader/issues/1136.
Заключение
Если вы заботитесь о производительности или работаете с очень большими приложениями, попробуйте на практике создать/использовать функциональные компоненты. Небольшая трата времени на обучение стоит этого.
Ссылки:
https://vuejs.org/v2/guide/render-function.html#Functional-Components
https://itnext.io/whats-the-deal-with-functional-components-in-vue-js-513a31eb72b0