Важным принципом программирования программного обеспечения является D.R.Y (не повторяйся), который заключается в максимально возможном повторном использовании кода и логики для уменьшения повторения. Расширения компонентов позволяют избежать дублирования кода, упрощая разработку и быстрое обслуживание. Итак, как лучше всего расширить компоненты Vue?
Vue предоставляет множество API и шаблонов для поддержки повторного использования и расширения компонентов, которые вы можете выбрать в соответствии со своими целями и предпочтениями.
В этой статье представлены несколько наиболее распространенных методов и шаблонов, которые, я надеюсь, помогут вам.
Нужны ли расширения?
Имейте в виду, что все методы расширения компонентов добавляют сложности, добавляют дополнительный код и иногда снижают производительность.
Поэтому, прежде чем принимать решение о расширении компонента, лучше всего посмотреть, существуют ли другие более простые шаблоны проектирования, которые позволяют достичь этой цели.
Для замены компонентов расширения обычно достаточно следующих шаблонов проектирования компонентов:
-
propsС шаблонной логикой - слот слот
- Вспомогательные функции JavaScript
propsС шаблонной логикой
Самый простой способpropsВ сочетании с условным рендерингом шаблона реализуется многофункциональность компонента.
например, черезtypeАтрибуты:MyVersatileComponent.vue
<template>
<div class="wrapper">
<div v-if="type === 'a'">...</div>
<div v-else-if="type === 'b'">...</div>
<!--etc etc-->
</div>
</template>
<script>
export default {
props: { type: String },
...
}
</script>
При использовании компонентов передавайте разныеtypeЗначение может достигать различных результатов.
// *ParentComponent.vue*
<template>
<MyVersatileComponent type="a" />
<MyVersatileComponent type="b" />
</template>
Если есть два случая, этот режим неприменим или используется неправильно:
- Шаблон композиции компонентов разбивает состояние и логику на атомарные части, тем самым делая приложения масштабируемыми. Если в компоненте много условных суждений, читабельность и ремонтопригодность будут плохими.
- Первоначальная цель реквизита и логики шаблона — сделать компоненты динамическими, но есть также потребление ресурсов во время выполнения. Если вы используете этот механизм для решения проблем с композицией кода во время выполнения, это антишаблон.
слот
Другой способ избежать масштабирования компонентов — использоватьслоты, заключается в том, чтобы позволить родительскому компоненту устанавливать настраиваемый контент внутри дочернего компонента.
// *MyVersatileComponent.vue*
<template>
<div class="wrapper">
<h3>Common markup</div>
<slot />
</div>
</template>
// *ParentComponent.vue*
<template>
<MyVersatileComponent>
<h4>Inserting into the slot</h4>
</MyVersatileComponent>
</template>
Результат рендеринга:
<div class="wrapper">
<h3>Common markup</div>
<h4>Inserting into the slot</h4>
</div>
Одним из потенциальных ограничений этого шаблона является то, что элементы внутри слота подчинены контексту родительского компонента, что может быть неестественным при разделении логики и состояния.scoped slotЭто будет более гибко и будет упомянуто позже в разделе о неотрендеренных компонентах.
Вспомогательные функции JavaScript
Если вам нужно повторно использовать только независимые функции между компонентами, вам нужно только извлечь эти модули JavaScript, и вам вообще не нужно использовать режим расширения компонента.
Модульная система JavaScript — это очень гибкий и надежный способ совместного использования кода, поэтому вы должны полагаться на него как можно больше.MyUtilityFunction.js
export default function () {
...
}
MyComponent.vue
import MyUtilityFunction from "./MyUtilityFunction";
export default {
methods: {
MyUtilityFunction
}
}
Несколько режимов компонентов расширения
Если вы рассмотрели приведенные выше простые шаблоны, но эти шаблоны недостаточно гибки, чтобы удовлетворить ваши потребности. Затем вы можете рассмотреть возможность расширения компонента.
Существует четыре наиболее популярных способа расширения компонентов Vue:
У каждого метода есть свои плюсы и минусы, и в зависимости от сценария использования есть более или менее применимые части.
Композиционная функция
Последнее решение для обмена состоянием и логикой между компонентами — Composition API. Это API, введенный в Vue 3, и его также можно использовать в качестве плагина в Vue 2.
Как и прежде, в объявлении объекта конфигурации определения компонентаdata,computed,methodsи другие свойства по-разному, Composition API передаетsetupФункции объявляют и возвращают эти конфигурации.
Например, объявите его как свойство конфигурации Vue 2.CounterКомпоненты такие:Counter.vue
<template>
<button @click="increment">
Count is: {{ count }}, double is: {{ double }}
</button>
<template>
<script>
export default {
data: () => ({
count: 0
}),
methods: {
increment() {
this.count++;
}
},
computed: {
double () {
return this.count * 2;
}
}
}
</script>
Рефакторинг этого компонента с помощью Composition API делает то же самое:Counter.vue
<template><!--as above--><template>
<script>
import { reactive, computed } from "vue";
export default {
setup() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
});
function increment() {
state.count++
}
return {
count,
double,
increment
}
}
}
</script>
Одним из основных преимуществ объявления компонентов с помощью Composition API является простота повторного использования и извлечения логики.
Дальнейший рефакторинг для переноса функций счетчика в модуль JavaScript.useCounter.jsсередина:useCounter.js
import { reactive, computed } from "vue";
export default function {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
});
function increment() {
state.count++
}
return {
count,
double,
increment
}
}
Доступ к функции счетчика теперь можно получить черезsetupФункции можно легко вводить в любой компонент Vue:MyComponent.vue
<template><!--as above--></template>
<script>
import useCounter from "./useCounter";
export default {
setup() {
const { count, double, increment } = useCounter();
return {
count,
double,
increment
}
}
}
</script>
Композиционные функции делают функциональность модульной и многократно используемой, а также являются самым простым и недорогим способом расширения компонентов.
Недостатки Composition API
Недостатки Composition API на самом деле ничего не значат — возможно, он выглядит немного многословным, а новое использование немного незнакомо некоторым разработчикам Vue.
Для обсуждения преимуществ и недостатков Composition API рекомендуем прочитать:When To Use The New Vue Composition API (And When Not To)
mixin
Если вы все еще используете Vue 2 или просто предпочитаете определять функциональность компонента в виде объектов конфигурации, вы можете использовать шаблон миксина. Миксин выделяет общую логику и состояние в отдельные объекты, которые объединяются с объектами, определенными внутри компонентов, использующих миксин.
Мы продолжаем использовать предыдущийCounterПример компонента с общей логикой и состояниемCounterMixin.jsв модуле.CounterMixin.js
export default {
data: () => ({
count: 0
}),
methods: {
increment() {
this.count++;
}
},
computed: {
double () {
return this.count * 2;
}
}
}
Использовать миксины тоже очень просто, просто импортируйте соответствующий модуль иmixinsПросто добавьте переменные в массив. Когда компонент инициализируется, объект примеси объединяется с объектом, определенным внутри компонента.MyComponent.vue
import CounterMixin from "./CounterMixin";
export default {
mixins: [CounterMixin],
methods: {
decrement() {
this.count--;
}
}
}
вариант слияния
Что делать, если параметры внутри компонента конфликтуют с миксином?
Например, определите встроенныйincrementметод, который имеет более высокий приоритет?MyComponent.vue
import CounterMixin from "./CounterMixin";
export default {
mixins: [CounterMixin],
methods: {
// 自带的 `increment`` 方法会覆盖 mixin 的`increment` 吗?
increment() { ... }
}
}
На этот раз пришло время поговорить о стратегии слияния Vue. В Vue есть набор правил, которые определяют, как обрабатывать параметры с одинаковыми именами.
Часто параметры, поставляемые с компонентом, переопределяют параметры миксина. Но есть и исключения, например, хуки жизненного цикла одного типа не перезаписываются напрямую, а помещаются в массив и выполняются последовательно.
Вы также можете пройтиПользовательская стратегия слиянияИзменить поведение по умолчанию.
Недостатки миксинов
Как шаблон для расширения компонентов миксины легко использовать для простых сценариев, но как только масштаб увеличивается, возникает проблема. Мало того, что вам нужно обратить внимание на проблему конфликтов имен (особенно сторонних миксинов), трудно понять, откуда берется функция при использовании нескольких компонентов миксина, а также трудно локализовать проблему.
компоненты более высокого порядка
Компоненты высшего порядка (HOC) — это концепция, заимствованная из React, которую также может использовать Vue.
Чтобы понять эту концепцию, давайте отложим компоненты в сторону и рассмотрим две простые функции JavaScript:increment а такжеdouble.
function increment(x) {
return x++;
}
function double(x) {
return x * 2;
}
Предположим, мы хотим добавить к обеим функциям функцию: вывести результат в консоль.
Для этого мы можем использоватьФункции высшего порядкарежим, создайте новыйaddLoggingФункция, которая принимает функцию в качестве аргумента и возвращает функцию с добавленной функциональностью.
function addLogging(fn) {
return function(x) {
const result = fn(x);
console.log("The result is: ", result);
return result;
};
}
const incrementWithLogging = addLogging(increment);
const doubleWithLogging = addLogging(double);
Как компоненты могут использовать преимущества этого шаблона? Точно так же мы создаем компонент более высокого порядка для рендеринга.Counterкомпонент, добавляяdecrementметоды как свойства экземпляра.
Фактический код более сложный, просто чтобы дать схематический псевдокод как:
import Counter from "./Counter";
// 伪代码
const CounterWithDecrement => ({
render(createElement) {
const options = {
decrement() {
this.count--;
}
}
return createElement(Counter, options);
}
});
Режим Hoc более лаконичен, чем Mixin, лучше масштабируется, но цена — это увеличение компонентов пакета, и это должно быть хитростью.
нет компонента рендеринга
Если вам нужно использовать одну и ту же логику и состояние для нескольких компонентов, но представление отличается, вы можете рассмотретьнет компонента рендерингамодель.
Для этого шаблона требуются два типа компонентов:логикаКомпоненты используются для объявления логики и состояния,экспонатКомпоненты используются для отображения данных.
логический компонент
или вернутьсяCounterНапример, предположим, что нам нужно повторно использовать этот компонент в нескольких местах, но разными способами.
СоздаватьCounterRenderless.jsИспользуется для определения логических компонентов, включая логику и состояние, но не шаблоны, а черезrenderобъявление функцииscoped slot.
scoped slotПредоставляет три свойства для использования родительским компонентом: состояниеcount,методincrementи вычисляемые свойстваdouble.CounterRenderless.js
export default {
data: () => ({
count: 0
}),
methods: {
increment() {
this.count++;
}
},
computed: {
double () {
return this.count * 2;
}
},
render() {
return this.$scopedSlots.default({
count: this.count,
double: this.double,
increment: this.toggleState,
})
}
}
здесьscoped slotявляется ключом к логическим компонентам в этом шаблоне.
Компоненты дисплея
ДалееКомпоненты дисплея, как потребитель компонента без рендеринга, предоставляющий определенный метод отображения.
Все теги элементов содержатся внутриscoped slotвнутри. Как видите, эти свойства используются так же, как если бы шаблон был размещен непосредственно в компоненте логики.CounterWithButton.vue
<template>
<counter-renderless slot-scope="{ count, double, increment }">
<div>Count is: {{ count }}</div>
<div>Double is: {{ double }}</div>
<button @click="increment">Increment</button>
</counter-renderless>
</template>
<script>
import CounterRenderless from "./CountRenderless";
export default {
components: {
CounterRenderless
}
}
</script>
Шаблон компонента без рендеринга очень гибкий и простой для понимания. Однако он не такой общий, как предыдущие методы, и может быть только один сценарий применения, который заключается в разработке библиотек компонентов.
Расширение шаблона
Независимо от того, имеет ли указанный выше API или шаблон проектирования ограничение, расширить компонент невозможно.шаблон. У Vue есть способ повторно использовать логику и состояние, но он ничего не может сделать с тегами шаблона.
Хитрый способ — использовать препроцессор HTML, такой как Pug, для обработки расширения шаблона.
Первым шагом является создание файла базового шаблона *.pug*, который содержит общие элементы страницы. также включитьblock input, в качестве заполнителя для расширения шаблона.
BaseTemplate.pug
div.wrapper
h3 {{ myCommonProp }} <!--common markup-->
block input <!--extended markup outlet -->
Чтобы иметь возможность расширить этот шаблон, необходимо установить плагин Pug для Vue Loader. Затем вы можете импортировать базовый шаблон и использоватьblock inputСинтаксис заменяет заполнитель:MyComponent.vue
<template lang="pug">
extends BaseTemplate.pug
block input
h4 {{ myLocalProp }} <!--gets included in the base template-->
</template>
Сначала вы можете подумать, что это та же концепция, что и слот, но с одним отличием: базовый шаблон здесь не принадлежит какому-либо отдельному компоненту. это ввремя компиляцииСлияние с текущим компонентом, а не слотомВремя выполнениязаменять.
Использованная литература:
Увидев этот довольно темпераментный логотип, почему вы не обращаете внимания?