(Перевод) Использование функциональных компонентов в Vue.js

Vue.js

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

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

В качестве примера возьмем следующий элемент управления вводом меток:

Этот компонент имеет несколько интересных особенностей:

  • Вы не можете добавлять повторяющиеся теги
  • Пустые теги не допускаются
  • Автоматически удалять пробелы с обеих сторон содержимого тега
  • Нажмите Enter, чтобы сохранить метку.
  • Нажмите на символ x, чтобы удалить метку

Если вам нужно использовать такой компонент в своем проекте, использование его в виде библиотеки и удаление логики, безусловно, сэкономит много времени и усилий.

Но что, если вам нужен другой стиль в это время?

Следующий компонент имеет то же поведение, что и предыдущий компонент, но макет значительно отличается:

https://codepen.io/adamwathan/pen/KomKNK

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

Ввиду вышеизложенной ситуации позвольте представить один из самых важных пунктов этой статьи.

Слоты с ограниченной областью действия

В Vue.js слоты — это элементы-заполнители в компонентах, которые заменяются содержимым, переданным от родительского компонента/потребителя.

Scoped slotsКак и обычный слот, но с возможностью передачи параметров от дочерних компонентов к родительским компонентам/потребителям.

Обычные слоты похожи на передачу фрагмента текста html в компонент, а слоты с ограниченной областью действия похожи на передачу функции обратного вызова компоненту, который может получать данные и возвращать HTML.

Передайте параметры родительскому компоненту, добавив реквизиты к элементу слота в дочернем компоненте. родительский компонент через деструктурированиеdestructuring slot-scopeДанные атрибута, полученные внутри, чтобы получить эти параметры.

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

поставив:linkprop добавлен к элементу слота в компоненте LinksList, компонент родительского элемента теперь может передаватьslot-scopeПолучите доступ к этим данным и используйте их в своем собственном слотовом модуле.

Тип свойства слота

Вы можете передать в слот любой тип, но я считаю наиболее полезным использовать один из следующих 3 типов данных.

Данные

Простейшими типами реквизитов слота являются типы данных: строки, числа, логические значения, массивы, объекты и т. д.

В нашем примере компонента списка ссылокlinkПримером типа data prop является объект с некоторыми свойствами.

Родительский компонент может отображать данные или решать, как их отображать.

Действия

Атрибут действия — это функция, предоставляемая дочерним компонентом, и родительский компонент может вызвать эту функцию, чтобы вызвать определенные действия в дочернем компоненте.

Например, мы можем передать родительский компонент abookmarkметод, который используется для добавления данной ссылки в закладки.

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

Привязки

Bindings — это набор свойств или событий прослушивания с использованиемv-bindилиv-on, привязанный к определенному элементу.

Они полезны, когда вы хотите инкапсулировать подробности о том, как взаимодействовать с данным элементом.

Например, мы предоставляемbookmarkButtonAttrsсвязывать иbookmarkButtonEventsПривязки используются для перемещения этих деталей к самому компоненту, вместо того, чтобы позволять потребляющему компоненту передавать их самостоятельно.v-showинструкция и@clickОбрабатывает логику кнопок для добавления в закладки.

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

Renderless Components

объяснение имени: Компоненты без рендеринга, дословно переводится как компоненты без рендеринга, я предпочитаю называть их функциональными компонентами (на которые ссылается название в реакции, далее в совокупности именуемые функциональными компонентами).

Функциональный компонент — это компонент, который не отображает HTML-текст.

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

Функциональный компонент отображает именно то, что вы ему передаете, без каких-либо дополнительных элементов.

Так почему это полезно?

Производительность и поведение разделительного слоя

Поскольку функциональные компоненты имеют дело только с состоянием и поведением, они не принимают никаких решений по дизайну и компоновке.

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

Ниже представлены все компоненты ввода меток, но на этот раз они поддерживаются функциональным компонентом.

Так как же это поддерживается?

Базовая структура функциональных компонентов

Функциональные компоненты предоставляют только слот с заданной областью, где потребители могут предоставить весь модуль, который они хотят отобразить.

Скелет базового функционального компонента выглядит так:

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

Любой родительский компонент/потребитель может уничтожить его в своем собственном шаблоне.slot-scopeсерединаexamplePropиспользовать.

Практический вариант использования

Давайте создадим функциональную версию элемента управления вводом метки с нуля.

Сначала нам нужно создать пустой компонент без рендеринга без слотов,

и статический родительский компонент без какого-либо взаимодействия, который затем передается в слот дочернего компонента,

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

список тегов

Во-первых, мы заменяем статический список динамическим списком.

Этот компонент ввода метки является пользовательским элементом управления формы, и этооригинальный примерТо же самое, эти теги должны быть в родительском компоненте и проходитьv-modelпривязать к компоненту.

Сначала мы добавляем свойство value к функциональному компоненту и передаем его в слот, называемый тегами.

Далее в родительский компонент добавимv-modelинструкция, отslot-scopeПолучить теги из , затем сделать用v-forинструкции по их повторению.

Атрибут tags slot — хороший пример атрибута данных.

убрать тэг

Затем, когда нажата кнопка X, удалите метку.

В функциональных компонентах мы добавимremoveTagметод и передать его как атрибут слота родительскому элементу.

Затем мы добавляем элемент кнопки в родительский компонент.@clickсобытие, это событие можно вызвать в текущей вкладкеremoveTagметод.

Атрибут слота removeTag является примером атрибута действия.

Нажмите Enter, чтобы добавить тег

Добавление новых тегов сложнее, чем в предыдущих двух примерах.

Чтобы понять почему, давайте сначала посмотрим, как реализованы традиционные компоненты.

Мы отслеживаем этот новый тег (до его добавления) в свойстве newTag, затем передаемv-modelПривяжите это свойство к входу.

Как только пользователь нажимает ввод, пока тег действителен, мы добавляем его в массив списка, а затем очищаем значение ввода.

Вопрос здесь в том, как мы передаем слот с прицеломv-modelсвязывать.

Что ж, если вы хорошо знаете Vue, вы должны знатьv-modelПо сути, это синтаксический сахар, который отвечает за привязку атрибута value к пропу с именем value, и в то же время, когда его событие ввода срабатывает, новое значение выбрасывается через пользовательское событие ввода.

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

  • добавить компонентnewTagатрибут данных
  • возвращает привязку к:valueизnewTagобязательное свойство
  • вернуть привязку@keydown.enterИспользуется для добавления меток и привязок@inputСвойство привязки события, используемое для обновления метки

Теперь нам просто нужно привязать эти свойства к элементу ввода родительского компонента;

Явное добавление новых тегов

В нашем текущем макете пользователь добавляет новую метку, вводя элемент ввода и нажимая клавишу ввода. Но также легко подумать, что некоторые пользователи хотели бы иметь возможность предлагать щелкнуть кнопку добавления, чтобы добавить теги.

Достичь этого очень просто, нам просто нужно передать область действия слотаaddTagссылка на метод.

При разработке подобных функциональных компонентов лучше предоставить больше реквизитов слота, чем меньше.

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

запустить демонстрацию

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

Этот фактический компонент не содержит никакого html-текста, а родительский компонент, в котором мы определяем шаблон, не содержит никакого поведения, близко ли это к совершенству?

изменить макет

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

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

Создайте свой собственный обернутый компонент

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

Всякий раз, когда вам нужен компонент ввода метки, вам нужно много писать.

Вместо этого мы начали с обычного способа написания:

Это легко исправить: создайте свой собственный обернутый компонент!

Вот как писать примитивы с точки зрения функциональных компонентов<tags-input>как выглядит компонент

Теперь вы можете использовать этот компонент где угодно, написав всего одну строку кода.

еще безумнее

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

Например, вот метод, который использует URL-адрес в качестве атрибута для получения данных json из этого URL-адреса и передачи данных ответа родительскому компоненту.fetch-dataКомпоненты:

Это лучший способ отправлять запросы ajax? Наверное, нет, но это действительно весело!

в заключении

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

Рассмотрите возможность использования этого режима, если:

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

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


Кроме того, разделение кода представления и бизнес-логики является лишь средством уменьшения связанности кода и, таким образом, повышения надежности кода.Глубокий уровень заключается в том, что компоненты должны соответствовать идее высокой связанности и низкой связанности.Другие средства, соответствующие эта идея также нравитсяИнверсия контроля(IOC), модель публикации-подписки и т. д., я думаю, чем больше вы будете писать код позже, тем больше вы должны развивать это осознание.В противном случае просто напишите бизнес-код и напишите код, отвечающий требованиям, и улучшение будет быть медленнее.

Перевод статьи не полностью переведен в соответствии с оригинальным текстом.Чтобы лучше понять его, я добавил немного своего понимания.Если вам понравилась моя статья, пожалуйста, поставьте лайк, чтобы выразить поддержку, или поделитесь ею с друзьями .

Ссылки по теме

Исходный код демо (форма компонента Vue с одним файлом)

Оригинальная ссылка

Инверсия управления МОК