[Основной синтаксис]
В JavaScript есть два основных способа объявления props:
Способ 1: Объектный синтаксис (наиболее распространенный)
< script setup>
const props = defineProps({
// 1. Простая проверка типа
title: String,
// 2. Несколько допустимых типов
userId: [String, Number],
// 3. Подробная конфигурация
count: {
type: Number,
default: 0,
required: false,
validator: (value) => value >= 0 && value < = 100
},
// 4. Объекты и массивы со значениями по умолчанию
user: {
type: Object,
default: () => ({ name: 'Гость', age: null })
},
// 5. Функции как props
onSubmit: Function,
// 6. Кастомный класс
date: Date
})
< /script>
Способ 2: Синтаксис с массивом строк (упрощенный)
< script setup>
// Только имена props без проверки типов
const props = defineProps(['title', 'count', 'items'])
< /script>
< template>
< div>{{ title }} - {{ count }}< /div>
< /template>
Детали каждой опции:
type — проверка типа данных
< script setup>
const props = defineProps({
// Все доступные встроенные типы:
name: String,
age: Number,
isActive: Boolean,
hobbies: Array,
config: Object,
birthDate: Date,
onUpdate: Function,
customInstance: CustomClass, // ваш собственный класс
// Или комбинация типов:
id: [String, Number, Symbol],
// Типы с конструкторами:
items: {
type: Array, default: () => []
}
})
< /script>
default — значение по умолчанию
< script setup>
const props = defineProps({
// Примитивные типы
count: {
type: Number,
default: 10
},
// Строки
message: {
type: String,
default: 'Привет, мир!'
},
// Булевы значения
isVisible: {
type: Boolean,
default: true
},
// ⚠️ ВАЖНО: Для объектов и массивов используйте функцию!
// ❌ Неправильно: // user: { type: Object, default: {} }
// ✅ Правильно:
user: {
type: Object,
default: () => ({ name: 'Аноним', role: 'user' })
},
items: {
type: Array,
default: () => []
},
// Функции по умолчанию
filterFn: {
type: Function,
default: () => (item) => true
}
})
< /script>
required — обязательный параметр
< script setup>
const props = defineProps({
// Обязательный параметр без значения по умолчанию
userId: {
type: String,
required: true
},
// Можно комбинировать required с default
theme: {
type: String,
required: true,
default: 'light' // будет использоваться, если parent не передан prop
}
})
< /script>
validator — кастомная (пользовательская) валидация
< script setup>
const props = defineProps({
// Простая валидация
age: {
type: Number,
validator: (value) => value >= 0 && value < = 120
},
// Валидация строки
status: {
type: String,
validator: (value) => ['active', 'inactive', 'pending'].includes(value)
},
// Валидация с параметром prop
password: {
type: String,
validator: (value) => {
// Проверка сложности пароля
if (value.length < 8) return false
if (!/[A-Z]/.test(value)) return false
if (!/[0-9]/.test(value)) return false
return true
}
},
// Валидация, зависящая от другого prop (нужен computed или watch)
endDate: {
type: Date,
validator: function(value) {
// this не работает в стрелочных функциях!
return value > this.startDate
}
}
})
< /script>
[Работа с props в компоненте]
Доступ к props:
< script setup>
const props = defineProps({
title: String,
count: Number,
items: Array
})
// Использование в JavaScript
console.log(props.title)
console.log(props.count)
console.log(props.items.length)
// Props реактивны - можно использовать в watch
import { watch } from 'vue'
watch(() => props.count, (newVal, oldVal) => {
console.log(`Count изменился с ${oldVal} на ${newVal}`)
})
// Props в computed свойствах
import { computed } from 'vue'
const doubledCount = computed(() => props.count * 2)
< /script>
< template>
< !-- Использование в шаблоне -->
< div>{{ title }}< /div>
< div v-for="item in items" :key="item.id">
{{ item.name }}
< /div>
< /template>
Преобразование имен (camelCase → kebab-case)
< !-- ChildComponent.vue -->
< script setup>
const props = defineProps({
userName: String, // camelCase в JS
itemCount: Number, // camelCase в JS
isDialogVisible: Boolean
})
< /script>
< !-- ParentComponent.vue -->
< template>
< !-- Используйте kebab-case в шаблоне -->
< child-component
user-name="Иван"
:item-count="5"
:is-dialog-visible="true"
/>
< /template>
[Особые случаи]
Передача всех props дочернему элементу:
< script setup>
const props = defineProps({
id: String,
class: String,
title: String,
// ... другие props
})
< /script>
< template>
< !-- Автоматическое связывание всех props -->
< div v-bind="props">
{{ title }}
< /div>
< !-- Или выборочно -->
< child-component v-bind="props" />
< /template>
Props с динамическими именами:
< script setup>
const props = defineProps({
// Динамические props через вычисляемое имя
['custom' + 'Prop']: String, // вычисляемое имя свойства
// Но обычно так не делают - лучше использовать статические имена
})
< /script>
Вложенная проверка объектов:
< script setup>
const props = defineProps({
// Для сложных структур используйте кастомный валидатор
config: {
type: Object,
default: () => ({}),
validator: (value) => {
// Проверка структуры объекта
if (!value.theme) return false
if (!['light', 'dark'].includes(value.theme)) return false
if (typeof value.autoSave !== 'boolean') return false
return true
}
}
})
< /script>
[Распространенные ошибки]
1. Не деструктурируйте props напрямую:
< script setup>
const props = defineProps({ title: String, count: Number })
// ❌ Плохо - потеря реактивности
const { title, count } = props
// ✅ Хорошо - сохраняйте ссылку на props
console.log(props.title)
// ✅ Альтернатива с toRefs
import { toRefs } from 'vue'
const { title, count } = toRefs(props)
// Теперь title и count реактивны!
< /script>
2. Не мутируйте props:
< script setup>
const props = defineProps({ items: Array })
// ❌ Плохо - мутация props
props.items.push('новый элемент')
// ✅ Хорошо - создайте локальную копию
import { ref, watch } from 'vue'
const localItems = ref([])
watch(() => props.items, (newItems) => {
localItems.value = [...newItems]
}, { immediate: true })
< /script>
3. Правильные значения по умолчанию:
< script setup>
const props = defineProps({
// ❌ Неправильно для объектов/массивов
user: { type: Object, default: {} },
items: { type: Array, default: [] },
// ✅ Правильно - используйте функцию
user: { type: Object, default: () => ({}) },
items: { type: Array, default: () => [] }
})
< /script>
[Полезные шаблоны]
Прокси для всех props:
< script setup>
// Принимаем все props родителя
const props = defineProps({
// Можно перечислить все возможные HTML атрибуты
id: String,
class: String,
style: [String, Object, Array],
title: String,
disabled: Boolean,
// ... и т.д.
})
// Или использовать пустое объявление для принятия любых props
const props = defineProps({})
< /script>
< template>
< div v-bind="$attrs">
< !-- Содержимое -->
< /div>
< /template>
Props с вычисляемыми значениями:
< script setup>
import { computed } from 'vue'
const props = defineProps({ price: Number, currency: {
type: String,
default: 'USD'
}
})
// Вычисляемое значение на основе props
const formattedPrice = computed(() => {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: props.currency
}).format(props.price)
})
< /script>
Комбинированные props:
< script setup>
const props = defineProps({
// Базовые props modelValue: {
type: [String, Number, Boolean, Array, Object],
default: null
},
// Конфигурационные props
options: {
type: Object,
default: () => ({})
},
// Состояния
disabled: Boolean,
readonly: Boolean,
// Валидация rules: {
type: Array,
default: () => []
}
})
< /script>
[Отладка и проверка]
< script setup>
const props = defineProps({
title: String,
count: Number
})
// Вывод props в консоль при изменении
import { watch } from 'vue'
import { useDevtools } from '@vue/devtools-api'
watch(props, (newProps) => {
console.log('Props обновились:', newProps)
// Интеграция с Vue DevTools
if (process.env.NODE_ENV === 'development') {
const devtools = useDevtools()
devtools.inspect(props)
}
}, { deep: true, immediate: true })
< /script>
[Полный пример компонента]
< script setup>
import { computed, watch } from 'vue'
// Объявление всех props
const props = defineProps({
// Идентификаторы
id: String, name: {
type: String,
required: true
},
// Состояния isActive: {
type: Boolean,
default: false
},
isLoading: {
type: Boolean,
default: false
},
isDisabled: Boolean,
// Данные
value: {
type: [String, Number, Boolean, Array, Object],
default: null
},
items: {
type: Array,
default: () => []
},
// Конфигурация
settings: {
type: Object,
default: () => ({
maxItems: 10,
sortable: true,
filterable: false
}),
validator: (value) => {
return value.maxItems > 0 && typeof value.sortable === 'boolean'
}
},
// События
onChange: Function,
onSelect: Function
})
// Реактивные вычисления на основе props
const itemCount = computed(() => props.items.length)
const isMaxReached = computed(() => itemCount.value >= props.settings.maxItems)
// Отслеживание изменений props
watch(() => props.isActive, (newVal) => {
if (newVal && props.onChange) {
props.onChange('activated')
}
})
// Локальное состояние на основе props
const localValue = ref(props.value)
watch(() => props.value, (newVal) => {
localValue.value = newVal
})
< /script>
< template>
< div
:id="id"
:class="[
'component',
{
'is-active': isActive,
'is-loading': isLoading,
'is-disabled': isDisabled || isMaxReached
}
]"
>
< h3>{{ name }}< /h3>
< p>Элементов: {{ itemCount }} / {{ settings.maxItems }}< /p>
< ul v-if="!isLoading">
< li
v-for="(item, index) in items"
:key="index"
@click="onSelect && onSelect(item)"
>
{{ item }}
< /li>
< /ul>
< div v-else>Загрузка...< /div>
< /div>
< /template>
< style scoped>
.component {
border: 1px solid #ccc; padding: 1rem;
}
.is-active {
border-color: blue;
}
.is-disabled {
opacity: 0.5; pointer-events: none;
}
< /style>
Итог: defineProps() в JavaScript предоставляет мощную, но гибкую систему типизации и валидации props. Ключевые моменты:
1. Используйте функции для значений по умолчания объектов и массивов.
2. Не мутируйте props напрямую.
3. Не деструктурируйте props без toRefs.
4. Используйте валидаторы для сложных проверок.
5. Помните о преобразовании имен из camelCase в kebab-case.