在Vue中,父组件可以往子组件中传递一些属性值。而子组件需要使用props配置项定义这些要从父组件中获取的数据。
使用props配置项,可以让该组件的标签在被使用时,通过组件标签属性将值传入组件中。
例如:
1<!-- SiteUser.vue -->
2<template>
3 <div>
4 <h2>姓名:{{name}}</h2>
5 <h2>年龄:{{age}}</h2>
6 <h2>性别:{{sex}}</h2>
7 </div>
8</template>
9<script>
10export default {
11 name: 'SiteUser',
12 // 声明需要接收的属性
13 props: [
14 'name',
15 'age',
16 'sex',
17 ],
18}
19</script>
1<!-- App.vue -->
2<template>
3 <div>
4 <!--
5 在标签中,根据对应的 props 属性名称
6 使用Html属性键值对的方式将值传入
7 -->
8 <SiteUser name="张三" age="21" sex="男"/>
9 <hr>
10 <SiteUser name="李四" age="18" sex="女"/>
11 <hr>
12 <SiteUser name="王五" age="28" sex="男"/>
13 </div>
14</template>
15
16<script>
17 // 引入组件
18 import SiteUser from './components/SiteUser.vue';
19
20 export default {
21 name: 'app',
22 components: {
23 SiteUser,
24 },
25 }
26</script>
属性值的传递
在组件标签中,使用键值对将属性作为props属性传递给组件时,默认传入的数据类型是作为字符串类型。这意味着使用默认的方式无法将值作为其它类型进行传递。
要传递其它类型的值,可以使用v-bind指令。这是因为v-bind指令会将属性的值作为表达式去执行并计算值。所以使用了v-bind指令就以为着可以为props属性传入不同类型的值。
如,将上例中的age作为Number类型传入:
1<SiteUser name="张三" :age="20 + 1" sex="男"/>
2<SiteUser name="李四" :age="18" sex="女"/>
3<SiteUser name="王五" :age="28" sex="男"/>
定义方式
props有3种定义方式:
-
字符串数组形式(简单声明):将所需传递的属性,作为字符串数组的元素进行声明。使用该方式进行声明的
props属性,不限制传递的类型,不限制传递的必要性。如上例中:
1props: [ 2 'name', 3 'age', 4 'sex', 5], -
仅类型限制形式:在传递的同时进行类型限制。如传递的类型不匹配,控制台会报错。
如上例,使用仅类型限制:
1props: { 2 name: String, 3 age: Number, 4 // age: [Number, String], // 允许指定多种类型 5 sex: String, 6}, -
多限制形式:在传递的同时可以进行类型限制、必要性限制或默认值限制。
如上例,使用多限制:
1props: { 2 name: { 3 type: String, // 类型限制 4 required: true, // 必须传递(默认为 false) 5 }, 6 age: { 7 type: Number, 8 default: 0, // 设置默认值 9 }, 10 sex: { 11 type: String, 12 required: true, 13 } 14},注:一般情况下,
required设置为true时,就不使用default;在设置required或required为false时,最好使用default。
props属性是只读的,Vue底层会对props属性进行监测,并限制其修改。对props的修改不一定会失败,但是如果进行了修改,Vue会在控制台发出警告。
如果业务需求确实需要修改props属性,可以在data属性或计算属性中添加props属性对应的不重名的拷贝,然后在data属性或计算属性之上对数据进行修改。
例如:
1<template>
2 <div>
3 <h2>姓名:{{myName}}</h2>
4 <h2 v-if="myAge > 0">年龄:{{myAge}}</h2>
5 <h2>性别:{{mySex}}</h2>
6 <button @click="changeSex">修改性别</button>
7 </div>
8</template>
9
10<script>
11export default {
12 name: 'SiteUser',
13 // 接收的同时对数据进行类型、必要性和默认值限制
14 props: {
15 name: {
16 type: String, // 类型限制
17 required: true, // 必须传递(默认为 false)
18 },
19 age: {
20 type: Number,
21 default: 0, // 设置默认值
22 },
23 sex: {
24 type: String,
25 required: true,
26 }
27 },
28 data() {
29 return {
30 // 可以重新定义不与 props 属性重名的 data 属性,以便操作数据
31 myName: this.name,
32 myAge: this.age,
33 mySex: this.sex,
34 }
35 },
36 methods: {
37 changeSex() {
38 // props 属性可以修改,但是控制台会报错,也过不了语法检查
39 // this.sex = this.sex === '男' ? '女' : '男'
40 this.mySex = this.mySex === '男' ? '女' : '男'
41 },
42 },
43}
44</script>
即,将props属性作为组件的初始化数据,对data属性或计算属性等内容进行初始化。组件中的数组载体还是以data属性或计算属性等为主。
注:
props属性同样不可与data属性或计算属性中的属性名称相同。- 如果
props属性和data属性重名,props属性优先级更高。props属性不能使用被Vue征用了的标签属性。props属性不能修改是相对于组件而言的。在组件中,不要对当前组件的props属性进行修改,但是在使用组件时,可以对子组件的props属性通过v-bind指令进行动态数据绑定。- 不要使用
v-model绑定props属性。
子组件到父组件的数据通信
组件间数据的通信涉及以下内容:
- 父组件到子组件的数据通信。
- 子组件到父组件的数据通信。
- 同级组件间的数据通信。
父组件到子组件的通信直接通过props属性就可以实现。而子组件到父组件的通信,也可以通过props实现。
修改对象类型的 Prop
Vue对props属性是一种浅层次的监测,Vue不会对props属性中的属性的改动进行监测。利用这一点可以在子组件中通过获取对象类型props属性值,然后利用v-model绑定该对象的属性来越过Vue的监测,从而实现子组件到父组件的通信。
1<!--
2 user-input.vue
3 -->
4
5<template>
6<div>
7 <!-- 使用 v-model 直接修改 props 对象数据类型的属性中的属性 -->
8 <input
9 type="text"
10 v-model.lazy="user.account"
11 placeholder="账号">
12 <br><br>
13 <input
14 type="password"
15 v-model.lazy="user.password"
16 placeholder="密码">
17</div>
18</template>
19
20<script>
21export default {
22 name: 'user-input',
23 props: [
24 "userInfo", // 将数据对象定义在父组件中
25 ],
26 data() {
27 return {
28 // 利用 props 属性初始化,目的是防止报错
29 user: this.userInfo,
30 }
31 },
32}
33</script>
1<!--
2 App.vue
3 -->
4
5<template>
6<div>
7 <user-input :userInfo="userInfo"/>
8 <br>
9 <button @click="login">登录</button>
10</div>
11</template>
12
13<script>
14import UserInput from './components/user-input.vue'
15
16export default {
17 name: 'app',
18 components: {
19 UserInput,
20 },
21 data() {
22 return {
23 userInfo: {
24 account: '',
25 password: '',
26 }
27 }
28 },
29 methods: {
30 login() {
31 alert(`账号:${this.userInfo.account}\n密码:${this.userInfo.password}`)
32 },
33 },
34}
35</script>
该方法其实就是子组件通过props,将用户在子组件中输入的数据,通过v-model双向绑定到父组件上。
不建议使用这种方法实现,虽然修改对象类型的props属性中的属性不会报错(绕过了Vue的监测),但是这样操作违反了Vue的原则。
传递函数类型的 Prop
借助props将父组件中的某些方法传递到子组件中,然后在子组件中通过调用这些从父组件传递过来的props方法来实现子组件到父组件的通信。
例如有一个user-input组件来收集用户的账号和密码信息,而App.vue需要获取user-input组件收集到的用户信息:
1<!--
2 user-input.vue
3 -->
4
5<template>
6<div>
7 <input
8 type="text"
9 v-model.lazy="userInfo.account"
10 placeholder="账号">
11 <br><br>
12 <input
13 type="password"
14 v-model.lazy="userInfo.password"
15 placeholder="密码">
16</div>
17</template>
18
19<script>
20export default {
21 name: 'user-input',
22 props: [
23 "setUserInfo", // 从父组件接收一个用于通知父组件数据更新的函数
24 ],
25 data() {
26 return {
27 userInfo: {
28 account: '',
29 password: '',
30 }
31 }
32 },
33 watch: {
34 // 监听 userInfo 对象
35 userInfo: {
36 deep: true,
37 handler() {
38 // 通知父组件 userInfo 被更新
39 this.setUserInfo(this.userInfo)
40 },
41 },
42 },
43}
44</script>
1<!--
2 App.vue
3 -->
4
5<template>
6<div>
7 <!-- 将通知信息更新的函数传递给组件 -->
8 <user-input :setUserInfo="setUserInfo"/>
9 <br>
10 <!-- 点击登录按钮显示用户信息 -->
11 <button @click="login">登录</button>
12</div>
13</template>
14
15<script>
16import UserInput from './components/user-input.vue'
17
18export default {
19 name: 'app',
20 components: {
21 UserInput,
22 },
23 data() {
24 return {
25 userInfo: {
26 account: '',
27 password: '',
28 }
29 }
30 },
31 methods: {
32 // 通知组件用户信息被更新
33 setUserInfo(userInfo) {
34 this.userInfo.account = Object.hasOwn(userInfo, "account") ?
35 userInfo.account : ""
36 this.userInfo.password = Object.hasOwn(userInfo, "password") ?
37 userInfo.password : ""
38 },
39 login() {
40 alert(`账号:${this.userInfo.account}\n密码:${this.userInfo.password}`)
41 },
42 },
43}
44</script>
该方法的原理就是:被父组件传递给子组件用于更新的函数,它的this指向的仍是父组件的实例对象。简单来说就是子组件调用了一个父组件的方法来通知父组件。
评论