Входные параметры
Эта страница предполагает, что вы уже прочитали Основы компонентов. Сначала прочитайте это, если вы новичок в работе с компонентами.
Объявление входных параметров
Компоненты Vue требуют явного объявления входных параметров, чтобы Vue знал, какие внешние входные параметры, переданны компоненту, следует рассматривать как обычные атрибуты (которые будут рассмотрены в отдельном разделе).
В SFC использующих <script setup>
, входные параметры могут быть объявлены с помощью макроса defineProps()
:
vue
<script setup>
const props = defineProps(['foo'])
console.log(props.foo)
</script>
В компонентах не имеющих<script setup>
, входные параметры объявляются с помощью props
:
js
export default {
props: ['foo'],
setup(props) {
// setup() получает входной параметр в качестве первого аргумента.
console.log(props.foo)
}
}
Обратите внимание, что аргумент, передаваемый в defineProps()
, совпадает со значением, предоставляемым props
: один и тот же API props разделяется между двумя стилями декларации.
В дополнение к объявлению входных параметров с использованием массива строк, мы также можем использовать объектный синтаксис:
js
// С использованием <script setup>
defineProps({
title: String,
likes: Number
})
js
// Без использования <script setup>
export default {
props: {
title: String,
likes: Number
}
}
Для каждого свойства в синтаксисе объявления объекта ключом является имя входного параметра, а значением должна быть функция-конструктор ожидаемого типа.
Это не только документирует ваш компонент, но и предупредит других разработчиков, использующих ваш компонент, в консоли браузера, если они передадут неверный тип. Более подробно валидацию входных параметров мы рассмотрим далее на этой странице.
Если вы используете TypeScript с <script setup>
, можно также объявлять входные параметры с помощью аннотаций чистого типа:
vue
<script setup lang="ts">
defineProps<{
title?: string
likes?: number
}>()
</script>
Подробнее: Типизирование входных параметров
Reactive Props Destructure
Vue's reactivity system tracks state usage based on property access. E.g. when you access props.foo
in a computed getter or a watcher, the foo
prop gets tracked as a dependency.
So, given the following code:
js
const { foo } = defineProps(['foo'])
watchEffect(() => {
// runs only once before 3.5
// re-runs when the "foo" prop changes in 3.5+
console.log(foo)
})
In version 3.4 and below, foo
is an actual constant and will never change. In version 3.5 and above, Vue's compiler automatically prepends props.
when code in the same <script setup>
block accesses variables destructured from defineProps
. Therefore the code above becomes equivalent to the following:
js
const props = defineProps(['foo'])
watchEffect(() => {
// `foo` transformed to `props.foo` by the compiler
console.log(props.foo)
})
In addition, you can use JavaScript's native default value syntax to declare default values for the props. This is particularly useful when using the type-based props declaration:
ts
const { foo = 'hello' } = defineProps<{ foo?: string }>()
If you prefer to have more visual distinction between destructured props and normal variables in your IDE, Vue's VSCode extension provides a setting to enable inlay-hints for destructured props.
Passing Destructured Props into Functions
When we pass a destructured prop into a function, e.g.:
js
const { foo } = defineProps(['foo'])
watch(foo, /* ... */)
This will not work as expected because it is equivalent to watch(props.foo, ...)
- we are passing a value instead of a reactive data source to watch
. In fact, Vue's compiler will catch such cases and throw a warning.
Similar to how we can watch a normal prop with watch(() => props.foo, ...)
, we can watch a destructured prop also by wrapping it in a getter:
js
watch(() => foo, /* ... */)
In addition, this is the recommended approach when we need to pass a destructured prop into an external function while retaining reactivity:
js
useComposable(() => foo)
The external function can call the getter (or normalize it with toValue) when it needs to track changes of the provided prop, e.g. in a computed or watcher getter.
Детали передачи входных параметров
Именование входных параметров
Мы объявляем длинные имена свойств, используя camelCase, поскольку это позволяет избежать необходимости использовать кавычки при использовании их в качестве ключей свойств, а также позволяет нам ссылаться на них непосредственно в выражениях шаблона, поскольку они являются валидными идентификаторами JavaScript:
js
defineProps({
greetingMessage: String
})
template
<span>{{ greetingMessage }}</span>
Технически, вы также можете использовать camelCase при передаче входных параметров дочернему компоненту (за исключением шаблонов DOM). Тем не менее, общепринятым является использование kebab-case во всех случаях для согласования с атрибутами HTML:
template
<MyComponent greeting-message="hello" />
Мы используем PascalCase для тегов компонентов, когда это возможно, потому что это улучшает читаемость шаблона, отличая компоненты Vue от собственных элементов. Однако практическая польза от использования camelCase при передаче входных параметров не так велика, поэтому мы предпочитаем следовать соглашениям каждого языка.
Статические и динамические входные параметры
До сих пор вы встречали, что входные параметры передаются как статические значения, например:
template
<BlogPost title="Как изучить Vue" />
Вы также встречали входные параметры, присваивающие динамическое значение с помощью v-bind
или сокращения :
, например:
template
<!-- Динамически присваиваем значение переменной -->
<BlogPost :title="post.title" />
<!-- Динамически присваиваем значение комплексного выражения -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
Передача значений различных типов
В двух приведенных выше примерах мы передаем строковые значения, но любой тип значения может быть передан во входной параметр.
Числа
template
<!-- Несмотря на то, что `42` статическое значение, нам нужен v-bind, -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка. -->
<BlogPost :likes="42" />
<!-- Динамическое присвоение значения переменной. -->
<BlogPost :likes="post.likes" />
Булевы значения
template
<!-- Указание входного параметра без значения будет означать `true`. -->
<BlogPost is-published />
<!-- Несмотря на то, что `false` статическое значение, нам нужен v-bind, -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка. -->
<BlogPost :is-published="false" />
<!-- Динамическое присвоение значения переменной. -->
<BlogPost :is-published="post.isPublished" />
Массивы
template
<!-- Несмотря на то, что указан статический массив, нам нужен v-bind, -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка. -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- Динамическое присвоение значения переменной. -->
<BlogPost :comment-ids="post.commentIds" />
Объекты
template
<!-- Несмотря на то, что указан статический объект, нам нужен v-bind, -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка. -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- Динамическое присвоение значения переменной. -->
<BlogPost :author="post.author" />
Передача нескольких свойств с помощью объекта
Если хотите передать все свойства объекта в качестве входных параметров, то можно использовать v-bind
без аргументов (v-bind
вместо :prop-name
). Например, для объекта post
:
js
const post = {
id: 1,
title: 'Как изучить Vue'
}
Следующий шаблон:
template
<BlogPost v-bind="post" />
Будет аналогичен такой записи:
template
<BlogPost :id="post.id" :title="post.title" />
Однонаправленный поток данных
Все входные параметры образуют одностороннюю привязку между дочерним свойством и родительским: когда родительское свойство обновляется — оно будет передаваться дочернему, но не наоборот. Это предотвращает случайное изменение дочерними компонентами родительского состояния, что может затруднить понимание потока данных приложения.
Кроме того, каждый раз, когда обновляется родительский компонент, все входные параметры дочернего компонента будут обновлены актуальными значениями. Это означает, что не должны пытаться изменять входной параметр внутри дочернего компонента. Если это сделать, Vue отобразит предупреждение в консоли.
js
const props = defineProps(['foo'])
// ❌ предупреждение, входные параметры доступны только для чтения!
props.foo = 'bar'
Обычно встречаются два случая, когда возникает соблазн изменять входной параметр:
Входной параметр используется для передачи начального значения; дочерний компонент хочет использовать его как локальное свойство данных в дальнейшем. В этом случае лучше всего определить локальное свойство в данных, которое использует значение входного параметра в качестве начального:
jsconst props = defineProps(['initialCounter']) // counter использует только props.initialCounter в качестве начального значения; // он не будет получать будущие обновления входного параметра. const counter = ref(props.initialCounter)
Входной параметр передаётся как необработанное значение, которое необходимо преобразовать. В таком случае лучше всего объявить вычисляемое свойство с использованием входного параметра:
jsconst props = defineProps(['size']) // вычисляемое свойство автоматически обновится при изменении входного параметра const normalizedSize = computed(() => props.size.trim().toLowerCase())
Мутация объектов / массивов из входных параметров
Когда объекты и массивы передаются как входные параметры, дочерний компонент не может изменить привязку входного параметра, но он сможет изменить вложенные свойства объекта или массива. Это связано с тем, что в JavaScript объекты и массивы передаются по ссылке, и предотвращение таких мутаций для Vue неоправданно дорого.
Основным недостатком таких мутаций является то, что они позволяют дочернему компоненту влиять на родительское состояние способом, который не очевиден для родительского компонента, что может затруднить анализ потока данных в будущем. Для лучшей практики следует избегать таких мутаций, если только родительский и дочерний элементы не тесно связаны по замыслу. В большинстве случаев дочерний компонент должен выдать событие, чтобы позволить родительскому элементу выполнить мутацию.
Валидация входных параметров
Компоненты могут указывать требования к своим входным параметрам, такие как определение типа, которые уже видели выше. Если эти требования не выполнены — Vue предупредит сообщением в JavaScript-консоли браузера. Это особенно полезно при разработке компонента, который предназначен для использования другими.
Чтобы указать валидации входного параметра, вы можете предоставить в defineProps()
, объект с валидациями для проверки значения, вместо массива строк. Например:
js
defineProps({
// Базовая проверка типов
// (`null` и `undefined` проходят проверку для любого типа)
propA: Number,
// Несколько допустимых типов
propB: [String, Number],
// Обязательное значение строкового типа
propC: {
type: String,
required: true
},
// Обязательное, но нулевое значение строкового типа
propD: {
type: [String, null],
required: true
},
// Число со значением по умолчанию
propE: {
type: Number,
default: 100
},
// Объект со значением по умолчанию
propF: {
type: Object,
// Для объектов или массивов значения по умолчанию
// должны возвращаться из функции. Функция получает необработанные
// входные параметры, полученные компонентом в качестве аргумента.
default(rawProps) {
return { message: 'привет' }
}
},
// Пользовательская функция для валидации
// полный перечень входных параметров передается в качестве 2-го аргумента в 3.4+
propG: {
validator(value, props) {
// Значение должно соответствовать одной из этих строк
return ['success', 'warning', 'danger'].includes(value)
}
},
// Функция с значением по умолчанию
propH: {
type: Function,
// В отличие от объекта или массива по умолчанию, это не фабричная функция — это функция,
// служащая в качестве значения по умолчанию
default() {
return 'Функция по умолчанию'
}
}
})
Совет
Код внутри аргумента defineProps()
не может получить доступ к другим переменным, объявленным в <script setup>
поскольку при компиляции все выражение перемещается во внешнюю область видимости функции.
Дополнительные сведения:
Все входные параметры по умолчанию являются необязательными, если не указано
required: true
.Отсутствующий необязательный входной параметр, отличный от
Boolean
будет иметь значениеundefined
.Отсутствующий входной параметр
Boolean
будет приведен кfalse
. Вы можете изменить это, задав для него значениепо умолчанию
. Например:default: undefined
, чтобы он вел себя как не булево значение.Если указано значение
по умолчанию
, оно будет использоваться, если разрешенное значение входного параметраundefined
- это включает в себя как отсутствие входного параметра, так и явноеundefined
значение.
Когда проверка свойства завершается неудачей, Vue выдает консольное предупреждение (если используется сборка для разработки).
Если вы используете объявления свойств на основе типов , Vue сделает все возможное, чтобы скомпилировать аннотации типов в эквивалентные объявления свойств во время выполнения. Например, defineProps<{ msg: string }>
будет скомпилирован в { msg: { type: String, required: true }}
.
Проверка типа во время выполнения
Значением type
может быть одним из следующих собственных конструкторов:
String
Number
Boolean
Array
Object
Date
Function
Symbol
Error
Кроме того, type
может быть пользовательским классом или функцией конструктора, и утверждение будет сделано с помощью проверки instanceof
. Например, дан следующий класс:
js
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
Вы можете использовать его как тип входного параметра:
js
defineProps({
author: Person
})
Vue будет использовать instanceof Person
для проверки того, действительно ли значение входного параметра author
является экземпляром класса Person
.
Обнуляемый Тип
Если тип является обязательным, но допускает значение null, вы можете использовать синтаксис массива, который включает null
:
js
defineProps({
id: {
type: [String, null],
required: true
}
})
Обратите внимание, что если type
просто равен null
без использования синтаксиса массива, то будет разрешен любой тип.
Булево преобразование
Входные параметры с типом Boolean
имеют специальные правила приведения, чтобы имитировать поведение собственных булевых атрибутов. Дан <MyComponent>
со следующим объявлением:
js
defineProps({
disabled: Boolean
})
Компонент можно использовать следующим образом:
template
<!-- эквивалентно :disabled="true" -->
<MyComponent disabled />
<!-- эквивалентно :disabled="false" -->
<MyComponent />
Когда входной параметр объявлен с использованием нескольких типов, правила приведения для Boolean
также будут применяться. Однако есть нюанс - когда есть и String
, и Boolean
, то правило приведения для Boolean
применяется только в том случае, если Boolean
находится раньше, чем String
:
js
// disabled будет преобразован в true
defineProps({
disabled: [Boolean, Number]
})
// disabled будет преобразован в true
defineProps({
disabled: [Boolean, String]
})
// disabled будет преобразован в true
defineProps({
disabled: [Number, Boolean]
})
// disabled будет разобран как пустая строка (disabled="")
defineProps({
disabled: [String, Boolean]
})