Пропсы
Эта страница предполагает, что вы уже прочитали Основы компонентов.
Объявление пропсов
Компоненты Vue требуют явного объявления пропсов, чтобы Vue знал, какие внешние параметры, переданные компоненту, должны рассматриваться как передаваемые атрибуты.
В однофайловых компонентах, использующих <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
: один и тот же Options API используется совместно в двух стилях объявления.
Помимо объявления параметров с помощью массива строк, мы также можем использовать синтаксис объекта:
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>
Подробнее: Типизация пропсов компонента
Деструктуризация реактивных пропсов
Система реактивности Vue отслеживает использование состояний на основе доступа к свойствам. Например, когда вы обращаетесь к props.foo
в вычисляемом геттере или наблюдателе, параметр foo
отслеживается как зависимость.
Итак, приведём следующий код:
js
const { foo } = defineProps(['foo'])
watchEffect(() => {
// выполняется только один раз до 3.5
// перезапускается при изменении параметра "foo" в версии 3.5+
console.log(foo)
})
В версии 3.4 и ниже foo
является фактической константой и никогда не изменится. В версии 3.5 и выше компилятор Vue автоматически добавляет префикс props.
, когда код в одном блоке <script setup>
обращается к переменным, деструктурированным из defineProps
. Поэтому приведённый выше код становится эквивалентным следующему:
js
const props = defineProps(['foo'])
watchEffect(() => {
// `foo` преобразуется компилятором в `props.foo`
console.log(props.foo)
})
Кроме того, вы можете использовать собственный синтаксис JavaScript для объявления значений по умолчанию для параметров. Это особенно полезно при использовании объявления параметров на основе типов:
ts
const { foo = 'hello' } = defineProps<{ foo?: string }>()
Если вы предпочитаете визуально отличать деструктурированные параметры от обычных переменных в вашей IDE, расширение Vue VSCode предоставляет настройку, позволяющую включить подсказки для деструктурированных параметров.
Передача деструктурированных пропсов в функции
Когда мы передаем деструктурированный параметр в функцию, например:
js
const { foo } = defineProps(['foo'])
watch(foo /* ... */)
Это не будет работать так, как ожидалось, потому что это эквивалентно watch(props.foo, ...)
— мы передаем значение watch
, а не реактивный источник данных. На самом деле, компилятор Vue будет отлавливать такие случаи и выдавать предупреждение.
Подобно тому, как мы можем наблюдать за обычным параметром с помощью watch(() => props.foo, ...)
, мы также можем наблюдать за деструктурированным параметром, обернув его в геттер:
js
watch(() => foo /* ... */)
Кроме того, это рекомендуемый подход, когда нам нужно передать деструктурированный параметр во внешнюю функцию, сохранив при этом реактивность:
js
useComposable(() => foo)
Внешняя функция может вызвать геттер (или нормализовать его с помощью toValue), когда ей нужно отследить изменения предоставленного параметра, например, в вычисляемом или наблюдающем геттере.
Подробности передачи пропсов
Регистр имён пропсов
Мы объявляем длинные имена пропсов, используя «верблюжий» регистр (camelCase
), потому что это позволяет избежать необходимости использовать кавычки при использовании их в качестве ключей свойств, а также позволяет нам ссылаться на них непосредственно в выражениях шаблона, поскольку они являются действительными идентификаторами JavaScript:
js
defineProps({
greetingMessage: String
})
template
<span>{{ greetingMessage }}</span>
Технически, вы также можете использовать «верблюжий» регистр (camelCase
) при передаче параметров дочернему компоненту (за исключением DOM шаблонов в DOM). Тем не менее, для согласования с атрибутами HTML принято использовать «шашлычный» регистр (kebab-case
) во всех случаях:
template
<MyComponent greeting-message="привет" />
Мы используем PascalCase для тегов компонентов, когда это возможно, потому что это улучшает читаемость шаблона, отличая компоненты Vue от собственных элементов. Однако практической пользы от использования «верблюжьего» регистра при передаче параметров не так много, поэтому мы предпочитаем следовать соглашениям каждого языка.
Сравнение статических и динамических пропсов
До сих пор вы видели, что параметры передаются как статические значения, как в примере:
template
<BlogPost title="Мое путешествие с Vue" />
Вы также видели, как параметры назначаются динамически с помощью v-bind
или его сокращения :
, как, например, в:
template
<!-- Динамическое присвоение значения переменной -->
<BlogPost :title="post.title" />
<!-- Динамически присваивайте значение сложному выражению -->
<BlogPost :title="post.title + ' от ' + post.author.name" />
Передача различных типов значений
В двух приведённых выше примерах мы передаем строковые значения, но в prop можно передать любой тип значения.
Число
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: 'Вероника',
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'
Обычно есть два случая, когда возникает соблазн мутировать параметр:
Свойство prop используется для передачи начального значения; дочерний компонент хочет использовать его в качестве локального свойства данных после этого. В этом случае лучше всего определить локальное свойство данных, которое использует параметр в качестве начального значения:
jsconst props = defineProps(['initialCounter']) // счётчик использует только 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
},
// Обязательная, но допускающая значение NULL строка
propD: {
type: [String, null],
required: true
},
// Число со значением по умолчанию
propE: {
type: Number,
default: 100
},
// Объект со значением по умолчанию
propF: {
type: Object,
// Объект или массив по умолчанию должен быть возвращен из
// фабричной функции. Функция получает необработанный
// параметр, полученный компонентом в качестве аргумента.
default(rawProps) {
return { message: 'привет' }
}
},
// Пользовательская функция валидатора
propG: {
validator(value) {
// Значение должно соответствовать одной из этих строк
return ['success', 'warning', 'danger'].includes(value)
}
},
// Функция со значением по умолчанию
propH: {
type: Function,
// В отличие от объекта или массива по умолчанию, это не фабричная функция
// function - это функция, служащая значением по умолчанию.
default() {
return 'Default function'
}
}
})
Примечание
Код внутри аргумента defineProps()
не может получить доступ к другим переменным, объявленным в <script setup>
, поскольку при компиляции всё выражение перемещается во внешнюю область видимости функции.
Дополнительные детали:
Все параметры по умолчанию необязательны, если только не указано
required: true
.Отсутствующий необязательный параметр, отличный от
Boolean
, будет иметь значениеundefined
.Отсутствующий параметр
Boolean
будет приведен к значениюfalse
. Вы можете изменить это, установив для него значениеdefault: undefined
, чтобы он вёл себя как небулевский параметр.Если указано значение
default
, оно будет использоваться, если разрешённое значение prop будетundefined
— это относится и к случаям, когда prop отсутствует, и к случаям, когда передано явное значение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, вы можете использовать синтаксис массива, включающий 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
— правило приведения булевых значений применяется только в том случае, если булевое значение появляется перед 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]
})