Vue2里的props怎么用?从声明到传值避坑全解析
不少刚开始学Vue2的同学,一碰到props就犯愁——到底咋声明?父组件咋传数据?传的时候类型不对咋办?还有单向数据流是啥意思?别慌,这篇把props从基础到进阶的知识点拆成问答,帮你把每个环节吃透~
props到底是干啥的?
props是Vue里父组件给子组件传数据的核心方式,你可以理解成“子组件对外暴露的‘接口’”,父组件通过这个接口给子组件塞数据,让同一个子组件在不同场景下显示不同内容。
举个实际例子:你做了个「商品卡片组件」,首页、分类页都要用它,这时候父组件(首页)把商品名称、价格这些数据通过props传给子组件(商品卡片),子组件只负责渲染,这样一个组件就能在不同页面复用,数据由父组件控制,是不是很灵活?
怎么在子组件里声明props?
有两种声明风格:简单数组式和对象验证式,场景不同用法也不同~
数组式(快速声明,无验证)
如果只是简单传值,不需要验证类型、必填这些,直接用数组列出来要接收的prop名称:
Vue.component('Child', {
props: ['name', 'age', 'isVip']
})
这种写法轻快,但缺点是没法做任何验证,适合小项目或临时组件。
对象式(带验证,严谨开发必用)
当你需要控制prop的类型、是否必填、默认值、自定义验证逻辑时,得用对象格式,每个prop对应一个配置项,常见配置有这几个:
- type:指定prop的类型,比如
String、Number、Boolean、Array、Object,甚至自定义构造函数(比如自己写的class); - required:布尔值,标记这个prop是否必须由父组件传入;
- default:当父组件没传这个prop时,用啥默认值(注意:对象/数组的
default要写成函数返回值,后面讲原因); - validator:自定义验证函数,传入prop的值,返回布尔值判断是否合法。
举个完整例子,做一个「用户信息组件」,要求name必填(字符串)、age选填(数字,默认18)、tags必须是数组且长度≥1:
Vue.component('UserCard', {
props: {
name: {
type: String,
required: true // 父组件必须传name
},
age: {
type: Number,
default: 18 // 没传就用18
},
tags: {
type: Array,
// 数组/对象的default要返回函数,避免所有实例共享同一个引用
default: () => [],
validator: (value) => { // 验证tags长度
return value.length >= 1
}
}
}
})
这样写后,父组件传值不对时,开发环境会直接在控制台报错提醒,相当于给代码加了“安全锁”~
父组件怎么给子组件传props?
父传子的方式分静态传递和动态传递,还有传对象/数组的技巧,一个个说:
静态传递(传固定值)
如果传给子组件的是“死数据”(比如固定字符串、数字),直接在标签上写属性:
<Child name="张三" age="18" />
注意哦!这种写法下,值的类型是字符串!哪怕你写age="18",子组件里如果声明type是Number,这里传的还是字符串'18',所以静态传递只适合字符串类型,或者子组件不验证类型的场景。
动态传递(传变量/表达式)
如果要传父组件的变量、布尔值、数组、对象,或者控制类型,得用v-bind(简写)绑定JS表达式:
<!-- 传变量 --> <Child :name="parentName" :age="parentAge" /> <!-- 传布尔值(加:`才是布尔true,否则是字符串'true') --> <Child :isVip="true" /> <!-- 传表达式 --> <Child :score="Math.random() * 100" />
用绑定后,值的类型由JS表达式决定(比如parentAge是数字,传过去就是数字;Math.random()返回数字,传过去也是数字),这样才能和子组件的type验证对应上~
批量传对象/数组
如果父组件有个对象,里面包含子组件需要的多个props,不用一个个传,直接用v-bind="对象"批量传递:
// 父组件data里的对象
data() {
return {
userInfo: {
name: '李四',
age: 22,
isVip: false
}
}
}
<!-- 子组件需要name、age、isVip三个props --> <Child v-bind="userInfo" /> <!-- 等价于分别传: <Child :name="userInfo.name" :age="userInfo.age" :isVip="userInfo.isVip" /> -->
这种写法在对象属性多的时候超省心,代码也更简洁~
props的验证规则怎么玩?
前面提了type、required、default、validator这些配置,这里展开讲讲细节,避免踩坑:
type可以玩出花
type不止支持内置类型(String/Number等),还能:
- 多个类型:比如
type: [String, Number],表示这个prop可以是字符串或数字; - 自定义构造函数:比如你写了个
class Person,type: Person,父组件传new Person()进来才会验证通过;
举个自定义构造函数的例子:
// 定义Person类
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
// 子组件props验证
props: {
user: {
type: Person, // 只有传Person实例才合法
required: true
}
}
// 父组件传值
<Child :user="new Person('王五', 25)" />
default的“坑”:对象/数组必须用函数
如果prop的类型是Object或Array,直接写default: {name: '默认'} 会出问题——所有子组件实例会共享同一个对象/数组引用!比如一个子组件改了这个对象的属性,其他子组件的默认值也会被改掉,这是引用类型的特性导致的。
所以正确写法是用函数返回默认值,每个实例都能拿到独立的副本:
props: {
settings: {
type: Object,
// 错误写法:default: { theme: 'light' }
// 正确写法:
default: () => ({ theme: 'light' })
},
hobbies: {
type: Array,
default: () => []
}
}
validator:自定义“安检员”
validator是个函数,接收当前prop的值作为参数,返回true/false表示是否合法,比如验证手机号格式:
props: {
phone: {
type: String,
validator: (value) => {
// 正则判断是否是11位手机号
return /^1\d{10}$/.test(value)
}
}
}
如果父组件传了个不符合规则的手机号,开发环境控制台会直接报错,相当于给数据加了“格式过滤器”~
props的单向数据流是啥?为啥不能直接改props?
Vue里props遵循“单向数据流”规则:父组件传值给子组件后,子组件不能直接修改props的值,为啥要这么设计?
想象一下:如果子组件能直接改props,父组件里的数据源就会被偷偷修改,整个项目的数据流向会变得混乱,调试时根本不知道谁改了数据,所以Vue强制让数据“父→子”单向流动,保证数据可预测。
那子组件要改props的值咋办?分两种场景:
只是“用”props的值,但需要局部修改
把props的值复制到子组件的data里,后续改data里的副本:
Vue.component('Child', {
props: ['count'],
data() {
return {
localCount: this.count // 复制props到data
}
},
methods: {
increment() {
this.localCount++ // 改的是localCount,不影响父组件
}
}
})
需要把修改同步回父组件
子组件通过$emit触发父组件的事件,让父组件自己修改数据源,从而更新子组件的props,比如子组件有个“+1”按钮,要让父组件的count加1:
// 子组件
Vue.component('Child', {
props: ['count'],
methods: {
handleClick() {
this.$emit('update-count', this.count + 1) // 触发事件,传新值
}
}
})
// 父组件
<template>
<div>
<Child :count="parentCount" @update-count="parentCount = $event" />
</div>
</template>
<script>
export default {
data() {
return {
parentCount: 0
}
}
}
</script>
这样父组件主动修改自己的parentCount,子组件的props会自动更新(因为单向数据流,父变了子跟着变),既遵守规则又实现了交互~
props和data、computed有啥区别?
很多同学刚学的时候会混淆这三个,其实它们的“身份”和作用域完全不同:
| 特性 | props | data | computed |
|---|---|---|---|
| 数据来源 | 父组件传入(外部) | 组件内部自己定义(内部) | 基于props、data等计算衍生(内部) |
| 可修改性 | 不能直接改(单向流) | 可以自由修改(内部状态) | 一般是只读(由依赖决定) |
| 典型场景 | 子组件接收父组件配置 | 组件内部的临时状态(比如表单输入) | 复杂逻辑的封装(比如购物车总价计算) |
举个生活例子:你点外卖,props像“商家给你的餐品”(外部给的),data像“你自己加的辣椒醋”(内部调的),computed像“算出这顿饭总共花多少钱”(基于餐品和调料计算的结果)~
传值时类型不匹配咋办?
最常见的坑是父组件传值类型和子组件type声明不一致,比如父传字符串"18",子组件要Number类型的age,这时候得注意传值方式:
- 如果是静态传递(没加),比如
<Child age="18" />,子组件拿到的是字符串'18',哪怕type声明Number也没用; - 如果是动态传递(加),比如
<Child :age="18" />,子组件拿到的是数字18,这时候type验证才能生效。
所以记住:想传非字符串类型,必须用v-bind绑定JS表达式!比如传布尔值true,得写:isVip="true",不然传的是字符串'true';传数组/对象同理,必须用绑定。
子组件能主动通知父组件更新props吗?
前面讲单向数据流时提过,子组件不能直接改props,但可以通过$emit事件让父组件自己改,流程是:
- 子组件触发
$emit('事件名', 新值) - 父组件监听这个事件,修改自己的数据源(比如
data里的变量) - 因为父组件的数据源变了,子组件的props会自动更新(单向流的“父变子变”)
举个实时搜索的例子:子组件是搜索输入框,输入内容后要让父组件的searchKey更新,从而重新请求数据,代码如下:
// 子组件(搜索框)
Vue.component('SearchInput', {
props: ['value'], // 父组件传的当前搜索关键词
methods: {
handleInput(e) {
const newKey = e.target.value
this.$emit('input', newKey) // 触发input事件,传新关键词
}
},
template: `<input :value="value" @input="handleInput" />`
})
// 父组件
<template>
<div>
<SearchInput :value="searchKey" @input="searchKey = $event" />
<button @click="fetchData">搜索</button>
</div>
</template>
<script>
export default {
data() {
return {
searchKey: ''
}
},
methods: {
fetchData() {
// 用searchKey发请求...
}
}
}
</script>
这样子组件输入时,通过$emit通知父组件改searchKey,父组件的searchKey变了,子组件的value(props)也会跟着变,完美绕开“直接改props”的坑~
props的驼峰命名和短横线命名咋处理?
HTML标签的属性名是不区分大小写的,所以Vue里有个“驼峰→短横线”的转换规则:
- 子组件props声明用驼峰命名(比如
userName); - 父组件模板中传值时,要用短横线命名(比如
user-name); - 但如果是用JS动态绑定(
:userName),因为在JS上下文里,驼峰是合法的,所以也能传。
举个例子:
// 子组件声明 props: ['userName'] // 父组件模板里的两种传法: <!-- 静态传,必须用短横线 --> <Child user-name="赵六" /> <!-- 动态传,驼峰或短横线都能识别,但建议和子组件声明一致 --> <Child :userName="name" /> <Child :user-name="name" />
简单说:模板里静态写属性名用短横线,动态绑定()时驼峰/短横线都行,但为了统一,建议和子组件props的命名风格一致~
props验证不通过会怎样?
在开发环境下,Vue会帮你“挑错”:如果prop的类型不对、required没传、validator返回false,控制台会弹出警告,直接告诉你哪里传错了,相当于有个“代码检查助手”;
但到了生产环境(打包后),为了性能,Vue会把props验证的代码删掉,所以验证只在开发时起作用,用来提前发现错误,避免线上bug~
看到这,你对Vue2的props应该从“一头雾水”变成“心里有数”了吧?记住核心逻辑:props是父传子的桥梁,声明时做好验证,传值时注意类型和绑定方式,修改时遵守单向数据流规则,多写几个组件练手,这些知识点自然就吃透啦~如果还有疑问,评论区随时喊我~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网

