В Vue 3 с Composition API props объявляются через defineProps(). Самый простой вариант — массив строк, но для продакшена нужна полноценная валидация:
// Полное объявление с валидацией
const props = defineProps({
title: {
type: String,
required: true,
},
count: {
type: Number,
default: 0,
},
status: {
type: String,
default: 'active',
validator: (value) => ['active', 'inactive', 'pending'].includes(value),
},
})Vue выводит предупреждения в консоль при нарушении валидации — только в режиме разработки.
При использовании TypeScript-синтаксиса для defineProps дефолтные значения задаются через withDefaults():
// TypeScript-стиль
const props = withDefaults(defineProps<{
title: string
count?: number
tags?: string[]
}>(), {
count: 0,
tags: () => [], // для массивов/объектов — фабричная функция!
})Обратите внимание: для массивов и объектов дефолтное значение должно быть **функцией** (как в data() Options API), иначе все экземпляры компонента будут делить одну ссылку.
Props типа Boolean имеют особое поведение: их можно передавать без значения:
<!-- Эти записи эквивалентны: -->
<MyButton disabled />
<MyButton :disabled="true" />
<!-- А это — тоже эквивалентно: -->
<MyButton />
<!-- :disabled="false" — не передан -->const props = defineProps({
disabled: {
type: Boolean,
default: false,
},
loading: Boolean, // Краткая запись, default = false
})Функция validator получает значение и должна вернуть true/false:
const props = defineProps({
age: {
type: Number,
validator: (val) => val >= 0 && val <= 150,
},
color: {
type: String,
validator: (val) => /^#[0-9A-Fa-f]{6}$/.test(val),
},
size: {
type: String,
default: 'md',
validator: (val) => ['xs', 'sm', 'md', 'lg', 'xl'].includes(val),
},
})required: true — Vue выдаст предупреждение, если prop не переданundefinedrequired: true и default — это противоречиеconst props = defineProps({
id: { type: Number, required: true }, // обязательный
label: { type: String, default: '' }, // необязательный с дефолтом
extra: String, // необязательный, может быть undefined
})const props = defineProps({
value: [String, Number], // строка или число
callback: [Function, null], // функция или null
})Система валидации props — аналог того, как Vue проверяет значения во время выполнения
// Реализуем упрощённую систему валидации props,
// аналогичную тому, что делает Vue внутри.
function createPropsValidator(schema) {
return function validateProps(props) {
const warnings = []
for (const [key, def] of Object.entries(schema)) {
const value = props[key]
const isDefined = value !== undefined && value !== null
// Проверка required
if (def.required && !isDefined) {
warnings.push(`[Props] Prop "${key}" обязателен, но не передан`)
continue
}
// Применяем default если не передан
if (!isDefined && def.default !== undefined) {
props[key] = typeof def.default === 'function'
? def.default()
: def.default
continue
}
if (!isDefined) continue
// Проверка типа
if (def.type) {
const types = Array.isArray(def.type) ? def.type : [def.type]
const typeNames = types.map(t => t.name)
const valid = types.some(t => {
if (t === Boolean) return typeof value === 'boolean'
if (t === String) return typeof value === 'string'
if (t === Number) return typeof value === 'number'
if (t === Array) return Array.isArray(value)
if (t === Object) return typeof value === 'object' && !Array.isArray(value)
return value instanceof t
})
if (!valid) {
warnings.push(`[Props] Prop "${key}": ожидался тип ${typeNames.join('|')}, получено ${typeof value}`)
}
}
// Проверка validator
if (def.validator && isDefined) {
if (!def.validator(value)) {
warnings.push(`[Props] Prop "${key}": значение "${value}" не прошло валидацию`)
}
}
}
return { props, warnings }
}
}
// Описание схемы (как в defineProps)
const validate = createPropsValidator({
title: { type: String, required: true },
count: { type: Number, default: 0 },
status: {
type: String,
default: 'active',
validator: v => ['active', 'inactive', 'pending'].includes(v),
},
tags: { type: Array, default: () => [] },
size: {
type: String,
default: 'md',
validator: v => ['sm', 'md', 'lg'].includes(v),
},
})
function renderComponent(rawProps) {
const { props, warnings } = validate({ ...rawProps })
warnings.forEach(w => console.warn(w))
console.log('Итоговые props:', props)
console.log('---')
}
console.log('=== Корректные props ===')
renderComponent({ title: 'Привет', count: 5, status: 'active' })
console.log('=== Отсутствует required ===')
renderComponent({ count: 3 })
console.log('=== Неверный тип ===')
renderComponent({ title: 'Тест', count: 'не число' })
console.log('=== Неверное значение validator ===')
renderComponent({ title: 'Тест', status: 'unknown', size: 'xxl' })
console.log('=== Дефолтные значения (массив — фабрика) ===')
const r1 = validate({ title: 'A' })
const r2 = validate({ title: 'B' })
console.log('Разные массивы?', r1.props.tags !== r2.props.tags) // true — не shared ref
В Vue 3 с Composition API props объявляются через defineProps(). Самый простой вариант — массив строк, но для продакшена нужна полноценная валидация:
// Полное объявление с валидацией
const props = defineProps({
title: {
type: String,
required: true,
},
count: {
type: Number,
default: 0,
},
status: {
type: String,
default: 'active',
validator: (value) => ['active', 'inactive', 'pending'].includes(value),
},
})Vue выводит предупреждения в консоль при нарушении валидации — только в режиме разработки.
При использовании TypeScript-синтаксиса для defineProps дефолтные значения задаются через withDefaults():
// TypeScript-стиль
const props = withDefaults(defineProps<{
title: string
count?: number
tags?: string[]
}>(), {
count: 0,
tags: () => [], // для массивов/объектов — фабричная функция!
})Обратите внимание: для массивов и объектов дефолтное значение должно быть **функцией** (как в data() Options API), иначе все экземпляры компонента будут делить одну ссылку.
Props типа Boolean имеют особое поведение: их можно передавать без значения:
<!-- Эти записи эквивалентны: -->
<MyButton disabled />
<MyButton :disabled="true" />
<!-- А это — тоже эквивалентно: -->
<MyButton />
<!-- :disabled="false" — не передан -->const props = defineProps({
disabled: {
type: Boolean,
default: false,
},
loading: Boolean, // Краткая запись, default = false
})Функция validator получает значение и должна вернуть true/false:
const props = defineProps({
age: {
type: Number,
validator: (val) => val >= 0 && val <= 150,
},
color: {
type: String,
validator: (val) => /^#[0-9A-Fa-f]{6}$/.test(val),
},
size: {
type: String,
default: 'md',
validator: (val) => ['xs', 'sm', 'md', 'lg', 'xl'].includes(val),
},
})required: true — Vue выдаст предупреждение, если prop не переданundefinedrequired: true и default — это противоречиеconst props = defineProps({
id: { type: Number, required: true }, // обязательный
label: { type: String, default: '' }, // необязательный с дефолтом
extra: String, // необязательный, может быть undefined
})const props = defineProps({
value: [String, Number], // строка или число
callback: [Function, null], // функция или null
})Система валидации props — аналог того, как Vue проверяет значения во время выполнения
// Реализуем упрощённую систему валидации props,
// аналогичную тому, что делает Vue внутри.
function createPropsValidator(schema) {
return function validateProps(props) {
const warnings = []
for (const [key, def] of Object.entries(schema)) {
const value = props[key]
const isDefined = value !== undefined && value !== null
// Проверка required
if (def.required && !isDefined) {
warnings.push(`[Props] Prop "${key}" обязателен, но не передан`)
continue
}
// Применяем default если не передан
if (!isDefined && def.default !== undefined) {
props[key] = typeof def.default === 'function'
? def.default()
: def.default
continue
}
if (!isDefined) continue
// Проверка типа
if (def.type) {
const types = Array.isArray(def.type) ? def.type : [def.type]
const typeNames = types.map(t => t.name)
const valid = types.some(t => {
if (t === Boolean) return typeof value === 'boolean'
if (t === String) return typeof value === 'string'
if (t === Number) return typeof value === 'number'
if (t === Array) return Array.isArray(value)
if (t === Object) return typeof value === 'object' && !Array.isArray(value)
return value instanceof t
})
if (!valid) {
warnings.push(`[Props] Prop "${key}": ожидался тип ${typeNames.join('|')}, получено ${typeof value}`)
}
}
// Проверка validator
if (def.validator && isDefined) {
if (!def.validator(value)) {
warnings.push(`[Props] Prop "${key}": значение "${value}" не прошло валидацию`)
}
}
}
return { props, warnings }
}
}
// Описание схемы (как в defineProps)
const validate = createPropsValidator({
title: { type: String, required: true },
count: { type: Number, default: 0 },
status: {
type: String,
default: 'active',
validator: v => ['active', 'inactive', 'pending'].includes(v),
},
tags: { type: Array, default: () => [] },
size: {
type: String,
default: 'md',
validator: v => ['sm', 'md', 'lg'].includes(v),
},
})
function renderComponent(rawProps) {
const { props, warnings } = validate({ ...rawProps })
warnings.forEach(w => console.warn(w))
console.log('Итоговые props:', props)
console.log('---')
}
console.log('=== Корректные props ===')
renderComponent({ title: 'Привет', count: 5, status: 'active' })
console.log('=== Отсутствует required ===')
renderComponent({ count: 3 })
console.log('=== Неверный тип ===')
renderComponent({ title: 'Тест', count: 'не число' })
console.log('=== Неверное значение validator ===')
renderComponent({ title: 'Тест', status: 'unknown', size: 'xxl' })
console.log('=== Дефолтные значения (массив — фабрика) ===')
const r1 = validate({ title: 'A' })
const r2 = validate({ title: 'B' })
console.log('Разные массивы?', r1.props.tags !== r2.props.tags) // true — не shared ref
Реализуй функцию `definePropsWithDefaults(schema, provided)`, которая принимает схему props (объект с полями type, default, required, validator) и переданные пользователем значения. Функция должна: применить дефолтные значения для отсутствующих props (для функций-фабрик — вызвать их), вернуть объект с итоговыми props. Дополнительно реализуй функцию `validateProp(key, def, value)`, которая возвращает массив строк-ошибок (пустой если всё ок).
В definePropsWithDefaults проверяй hasOwnProperty или оператором in, чтобы отличить undefined от "ключ не передан". Для default: если typeof def.default === "function" — вызови def.default(), иначе используй значение напрямую.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке