Есть ли в вашем приложении Vue компоненты, которые имеют одинаковые параметры или даже разметку шаблона?
Было бы неплохо создать базовый компонент с общими параметрами и разметкой, а затем расширить базовый компонент для создания подкомпонентов . Такая архитектура поможет вам применить принцип DRY в вашем коде (не повторяйте себя), который может сделать ваш код более читабельным и уменьшить вероятность ошибок.
Vue предоставляет некоторые функции, помогающие с наследованием компонентов, но вам также придется добавить немного своей изобретательности.
Пример: вопросы опроса
Вот простой опрос, сделанный с помощью Vue.js:
Вы заметите, что каждый вопрос имеет свой связанный тип ввода:
- Ввод текста
- Выберите вход
- Радио вход
Хорошей архитектурой было бы превращение каждого типа вопроса / ввода в отдельный, повторно используемый компонент. Я назвал их в соответствии с вышеизложенным:
SurveyInputText
SurveyInputSelect
SurveyInputRadio
Имеет смысл, что каждый вопрос / ввод является отдельным компонентом, потому что каждому нужна своя разметка (например, <input type="text">
vs <input type="radio">
), и каждому также нужны свои реквизиты, методы и т. Д. Тем не менее, эти компоненты будут иметь много общего:
- Вопрос.
- Функция проверки.
- Состояние ошибки.
- И т.п.
Поэтому я считаю, что это отличный вариант использования для расширения компонентов!
Базовый компонент
Каждый из подкомпонентов будет наследоваться от одного названного файлового компонента SurveyInputBase
. Обратите внимание на следующее:
question
Опора будет общим для каждого компонента. Мы могли бы добавить более распространенные опции, но давайте остановимся только на одном для этого простого примера.- Нам как-то нужно скопировать реквизиты из этого компонента в любой расширяющий компонент.
- Нам как-то нужно вставить разметку для разных входов внутри шаблона.
SurveyInputBase.vue
<template>
<div class="survey-base">
<h4>{{ question }}</h4>
<!--the appropriate input should go here-->
</div>
</template>
<script>
export default {
props: [ 'question' ],
}
</script>
Наследование опций компонента
На мгновение забыв о шаблоне, как мы можем получить каждый подчиненный компонент для наследования реквизита? Каждому понадобится question
как опора, так и свои уникальные реквизиты:
Это может быть достигнуто путем импорта базового компонента и указания на него с помощью extends
опции:
SurveyInputText.vue
<template>
<!--How to include the question here???-->
<input :placeholder="placeholder">
</template>
<script>
import SurveyInputBase from './SurveyInputBase.vue';
export default {
extends: SurveyInputBase,
props: [ 'placeholder' ],
}
</script>
Глядя в Vue Devtools, мы видим, что использование extends
действительно дало нам базовый реквизит нашего подкомпонента:
Стратегия слияния
Вам может быть интересно, как субкомпонент унаследовал question
реквизит вместо того, чтобы перезаписывать его. extends
Вариант реализует стратегию объединения , которая обеспечит ваши варианты комбинируются правильно. Например, если реквизиты имеют разные имена, они, очевидно, будут включены, но если они имеют одинаковое имя, Vue предпочтет субкомпонент.
Стратегия слияния также работает с другими параметрами, такими как методы, вычисляемые свойства и ловушки жизненного цикла, комбинируя их с похожей логикой. Проверьте документы на точную логику того, как Vue это делает, но если вам нужно, вы можете определить свою собственную стратегию.
Примечание: есть также возможность использовать
mixin
свойство в компоненте вместоextends
. Я предпочитаюextends
для этого варианта использования, так как он имеет немного другую стратегию слияния, которая дает опциям субкомпонентов более высокий приоритет.
Расширение шаблона
Расширить опции компонента довольно просто — а как же шаблон?
Стратегия слияния не работает с template
опцией. Мы либо наследуем базовый шаблон, либо определяем новый и перезаписываем его. Но как мы можем их объединить ?
Мое решение этого заключается в использовании препроцессора Pug . Он поставляется с include
и extends
вариантами , так что кажется, хорошо подходит для этого шаблона.
Базовый компонент
Во-первых, давайте конвертируем шаблон нашего базового компонента в синтаксис Pug:
<template lang="pug">
div.survey-base
h4 {{ question }}
block input
</template>
Обратите внимание на следующее:
- Мы добавляем
lang="pug"
в наш шаблон тег, чтобы Vue-Loader обрабатывал его как шаблон Pug (также не забудьте также добавить модуль Pug в ваш проектnpm i --save-dev pug
) - Мы используем,
block input
чтобы объявить выход для субкомпонентного контента.
Так что здесь немного грязно. Если мы хотим, чтобы наши дочерние компоненты расширяли этот шаблон, нам нужно поместить его в свой собственный файл:
SurveyInputBase.pug
div.survey-base
h4 {{ question }}
block input
Затем мы помещаем include
этот файл в наш базовый компонент, так что он все еще может использоваться как обычный автономный компонент:
SurveyInputBase.vue
<template lang="pug">
include SurveyInputBase.pug
</template>
<script>
export default {
props: [ 'question' ]
}
</script>
Обидно это делать, так как это как бы отрицательно сказывается на назначении компонентов «одного файла», но для этого случая использования, я думаю, оно того стоит. Вы могли бы, вероятно, сделать собственный загрузчик веб-пакетов, чтобы избежать необходимости делать это.
Подкомпонент
Теперь давайте также преобразуем шаблон нашего подкомпонента в Pug:
SurveyInputText.vue
<template lang="pug">
extends SurveyInputBase.pug
block input
input(type="text" :placeholder="placeholder")
</template>
<script>
import SurveyInputBase from './SurveyInputBase.vue';
export default {
extends: SurveyInputBase,
props: [ 'placeholder' ],
}
</script>
Подкомпоненты используют extends
функцию Pug, которая включает в себя базовый компонент и выводит любой пользовательский контент в input
блоке (концепция, аналогичная слотам ).
Вот как будет эффективно выглядеть шаблон подкомпонента после расширения базы и перевода обратно в обычный шаблон HTML Vue:
<div class="survey-base">
<h4>{% raw %}{{ question }}{% endraw %}</h4>
<input type="text" :placeholder="placeholder">
</div>
Собери все вместе
Используя эту стратегию , мы можем пойти дальше и создать две другие компоненты к югу SurveyInputSelect
и SurveyInputRadio
со своим собственным реквизитом и разметкой.
Если мы затем используем их в проекте, наш основной шаблон может выглядеть так:
<survey-input-text
question="1. What is your name?"
placeholder="e.g. John Smith"
></survey-input-text>
<survey-input-select
question="2. What is your favorite UI framework?"
:options="['React', 'Vue.js', 'Angular']"
></survey-input-select>
<survey-input-radio
question="3. What backend do you use?"
:options="['Node.js', 'Laravel', 'Ruby']"
name="backend"
>
</survey-input-radio>
А вот визуализированная разметка:
<div class="survey-base">
<h4>1. What is your name?</h4>
<input type="text" placeholder="e.g. John Smith">
</div>
<div class="survey-base">
<h4>2. What is your favorite UI framework?</h4>
<select>
<option>React</option>
<option>Vue.js</option>
<option>Angular</option>
</select>
</div>
<div class="survey-base">
<h4>3. What backend do you use?</h4>
<div><input type="radio" name="backend" value="Node.js">Node.js</div>
<div><input type="radio" name="backend" value="Laravel">Laravel</div>
<div><input type="radio" name="backend" value="Ruby">Ruby</div>
</div>
Примечание: мы могли бы также создать экземпляр
SurveyInputBase
компонента, так как он будет работать автономно, но в этом примере он не был действительно необходим. Я думал, что это важный момент, чтобы упомянуть, хотя.