Выяснение того, что, почему, когда и как работают с функциональными компонентами Vue.js.

Я потратил несколько дней, пытаясь осмыслить функциональные компоненты Vue.js. Возможно, для кого-то с опытом React или Angular это знакомый шаблон, но я обнаружил, что это ответы даже на некоторые основные вопросы, такие как:

  • Что такое функциональные компоненты?
  • Почему я должен переживать?
  • Когда мне их использовать?

оказался немного неуловимым. Итак, я потратил много времени на эту страницу документации Vue и несколько других разрозненных ресурсов, чтобы разобраться в сути вещей.

Что такое функциональные компоненты?

Изначально я зациклился на названии: «функциональный компонент». По своей наивности я подумал, что это звучит как компонент, предназначенный для выполнения чего-либо, а не как часть пользовательского интерфейса. Хотя это один из типов функциональных компонентов («компонент более высокого порядка» или «компонент-оболочка», который делегирует или украшает другие компоненты), существует другой, более простой вариант: немые компоненты. Я называю их тупицами, потому что у них нет собственных данных (они без состояния), ни своих собственных методов, ни даже экземпляра (то есть нет this и нет жизненного цикла).

Почему я должен переживать?

С функциональными компонентами вы получаете организационные, СУХИЕ преимущества компонента без накладных расходов на систему реактивности Vue. Это может улучшить производительность вашего приложения.

Когда мне следует использовать функциональные компоненты?

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

Итак, ответив на основные вопросы, я перешел к более сложному:

Как создать функциональные компоненты?

Чтобы разобраться в этом, я решил собрать идентичные компоненты 4 разными способами:

  • стандартный, обыденный однофайловый компонент (SFC)
  • стандартный компонент, созданный с помощью функции рендеринга (RF), а не шаблона
  • функциональный компонент, созданный с помощью функции рендеринга
  • функциональный однофайловый компонент (с использованием ключевого слова functional в шаблоне)

Мой пример несколько надуманный, чтобы исследовать различные части общей функциональности компонентов, в том числе:

  • запускающие события
  • используя слоты
  • используя реквизит
  • логика с v-if и v-for

Каждый компонент будет:

  • Выведите figure, содержащий img и, необязательно, figcaption и набор тегов.
  • Примите свойство «type», которое будет определять класс / стиль и изменяющуюся разметку.
  • Вызвать событие щелчка, которое запустит метод, указанный в узле компонента.

Конечный результат выглядит так:

Вот код файла App.vue:

Он довольно стандартный, в том числе:

  1. шаблон, который использует каждый из 4 компонентов
  2. блок скрипта, который импортирует компоненты и создает экземпляр App Vue с этими компонентами и парой методов.
  3. блок стилей (я переборщил со стилями, см. полный исходный код на Github.)

Прежде всего: стандартный однофайловый компонент

Первая версия компонента представляет собой стандартный однофайловый компонент:

Мы указываем нужную разметку и используем переданные свойства, которые мы определили с некоторой проверкой. Единственные примечательные элементы - это использование v-on:click="$emit('click’)" для генерации события щелчка (потому что мы хотим, чтобы запущенный метод работал в компоненте приложения, а не в этом дочернем компоненте), и использование v-if="$slots.default" для определения того, было ли что-нибудь (в нашем случае, заголовок) было передано для заполнения слота по умолчанию (см. vm.$slots документацию). В противном случае мы не показываем разметку figcaption, которая охватывает слот по умолчанию.

Далее: стандартный компонент, использующий функцию рендеринга

Следующий подход - стандартный компонент, использующий функцию рендеринга вместо шаблона.

Хотя концепции функциональных компонентов и функций рендеринга действительно полностью разделены, они тесно связаны в документации Vue.js (объяснение функциональных компонентов вложено на странице Функции рендеринга и JSX), и я видел очень несколько примеров функциональных компонентов с традиционным шаблоном. Так что нам лучше разобраться с функциями рендеринга.

Вот код:

Очевидно, что большое изменение состоит в том, что шаблон ушел, и вместо этого у нас есть свойство рендеринга. Его значение - функция render(createElement){}. createElement - это функция, которую мы можем использовать внутри функции рендеринга для построения дерева виртуальных узлов DOM (VNodes), которое в конечном итоге добавляется Vue к фактической DOM.

Функция createElement принимает 3 аргумента:

  1. строка создаваемого элемента html
  2. объект data, определяющий все различные атрибуты VNode
  3. и массив строк (для текстовых узлов) или других виртуальных узлов, которые будут заключены в созданный элемент.

Вы можете видеть в функции рендеринга выше, это довольно болезненный процесс для создания даже для относительно небольшого дерева. В этом примере мы:

  1. создать элемент img
  2. создать элемент figcaption, если в слоте по умолчанию есть контент
  3. создать разметку тегов, если теги передаются как опора
  4. передать все вышеперечисленное в массив в качестве третьего аргумента нашего последнего вызова createElement, который возвращает VNode оборачивающего элемента figure.

Уф.

Поскольку это всего лишь стандартный компонент, у него есть экземпляр, и мы можем использовать this для доступа к нашим props и $slots. Чтобы добиться поведения v-if из нашего шаблона, мы вручную проверяем this.$slots.default и возвращаем пустую строку, если ее нет. Там, где мы использовали v-for в шаблоне, здесь мы используем Array.map для возврата отдельного span VNode для каждого тега. Утомительно, если честно.

Вы можете повторно ввести шаблонный синтаксис с использованием JSX, что кажется немного безумным, но, возможно, в некоторых случаях имеет смысл.

Номер три: функциональный компонент, использующий функцию рендеринга

Третья версия - это наш первый функциональный компонент, о чем можно судить по свойству functional: true:

Этот компонент также использует функцию рендеринга, которая похожа на предыдущую, но с некоторыми важными отличиями. Вместо использования this мы используем второй аргумент, предоставленный функции рендеринга, context. Используя деструктуризацию объекта, мы извлекаем из объекта context только те свойства, которые нам нужны: props, listeners и slots.

Slots - это функция, которая возвращает объект с нашими различными слотами, поэтому там, где мы использовали $slots.default ранее, мы можем использовать slots().default.

Следует отметить несколько эзотерических вещей о двух свойствах объекта context. Один, children и slots() дают вам похожие, но немного разные вещи. О разнице читайте здесь. Во-вторых, listeners - это псевдоним для data.on.

Лучшее напоследок: функциональный однофайловый компонент

Последняя версия снова представляет собой однофайловый компонент, но на этот раз с использованием ключевого слова functional. Это было труднее всего найти информацию, но в конце концов. Ключевое слово functional упоминается в документации, но только мимоходом, с небольшими подробностями (так несвойственно!). В документации vue-loader есть этот дразнящий фрагмент.

Сравните это с первой версией стандартного компонента:

Вот ключевые различия, расположенные рядом:

Различия возникают из-за безэкземплярной природы функциональных компонентов. Без экземпляра мы не можем просто использовать наши имена свойств или $slots, которое является свойством экземпляра. Но, как и в случае с версией 3 выше, у нас есть доступ к объекту render context, который, как мы видели ранее, имеет объект props, объект listeners и функцию slots().

Для большинства «глупых» компонентов этот синтаксис, вероятно, легче всего написать и понять. У вас все еще есть довольно простой, похожий на html шаблон, хотя и немного более подробный, и ключевое слово functional вверху сразу же идентифицирует этот компонент как таковой. Таким образом, вы получаете преимущества единого файлового компонента без дополнительных затрат на полноценный компонент Vue. # выигрыш.

Заключение

Оказывается, в приложении, над которым я работал, полно кандидатов на глупые компоненты: небольшие, но часто повторяющиеся элементы, такие как кнопки, поля ввода форм, значки. Для любой части, которую вы хотите использовать повторно, но не требующей собственных реактивных данных, хорошим выбором будут SFC с ключевым словом functional. Использование функциональных компонентов может улучшить производительность вашего приложения, а запись их в виде отдельных файловых компонентов делает их удобными для чтения и более согласованными с другими вашими компонентами.

Но если вам нужен уровень контроля, который недоступен (или, что более вероятно, был бы очень подробным) в обычном шаблоне, тогда функция рендеринга может быть решением. Большинство примеров, которые я видел, включают вывод html, основанный на большом количестве условной логики, которая будет повторяться и ее будет трудно читать в форме шаблона.

Мысли? Оставьте добрый комментарий. Пока вы это делаете, как насчет пары хлопков, раз уж вы зашли так далеко?

Полезные ссылки