概述
props/$emit
是父子组件通讯的一种方式。
父组件通过 v-bind
绑定 prop
和 v-on
监听一个自定义事件。
<my-child :message="message" @myChange="change"></my-child>
子组件通过 props
接收父组件绑定的 prop
,使用 $emit
触发父组件监听的自定义函数。
// 接收值。
props: ["message"]
// 触发事件。
$emit("myChange");
通过以上步骤就能够实现父子组件的交互了。
示例:
- 父组件:
<template>
<my-child :message="message" @myChange="change"></my-child>
</template>
<script>
export default {
data() {
return {
message: "",
};
},
methods: {
change() {
console.log("change");
},
},
};
</script>
- 子组件:
<template>
<div>
{{ message }}
</div>
</template>
<script>
export default {
// 接收值。
props: ["message"],
// 触发父组件监听的自定义事件。
mounted() {
this.$emit("myChange");
},
};
</script>
单项数据流
父组件向子组件传递数据时,数据只能从父组件流向子组件,而不能反过来。
也就是说子组件不能直接修改 Props
接收过来的数据。只能通过 $emit
触发事件告知父组件进行修改。
父组件的数据发生更新后又会将最新的数据流下到子组件中。
如果接收的
Prop
是个对象类型的数据,可以对其property
进行过修改。只要不修改Prop
的引用地址Vue
就不会报错。如果
Prop
类型是基本数据类型,那么直接修改Prop
会报错。总的来说还是不推荐子组件直接修改
Prop
,这样会导致数据混淆。
Props
Props
Props
定义接收父组件通过 v-bind
绑定到自身的值,只有通过 Props
定义接收后子组件才能在实例中通过 this
去访问。例如,父组件通过 v-bind:msg="Hello,Vue!"
,子组件通过 Props
定义接收后,就能通过 this.msg
获取到传递过来的值了。
Props
的值可以是 数组
或是 对象
。
数组形式:
// 和 data 数据同级定义。
props: ["msg", "isOk"] // 在数组中定义需要接收的参数
Props 验证
通过设置规则,规定父元素传递过来的数据类型。该模式下的 Props
值是一个 对象
。
props: {
// 规定 msg 必须是字符串类型。
msg: String,
// 规定 count 必须是字符串或是数值类型。
count: [String, Number] // 多个类型需要用数组包裹。
}
上面例子中有 单个类型
和 多个类型
的情况。
Props
中的每一项 Prop
也可以设置为对象类型。
props: {
msg: {
type: String
},
count: {
type: [String, Number]
}
}
对象类型下,type
属性描述的就是 Prop
的类型。
在对象类型下还可以配置 required(必传)
和 default(默认值)
选项。
props: {
msg: {
type: String,
required: true // 必传
},
count: {
type: [String, Number],
default: 0 // 如果不传默认值为 0
}
}
如果 default(默认值)
设置的是 对象
或 数组
类型,需要通过一个工厂函数返回。
props: {
data: {
type: Object,
default: () => ({}) // 工厂函数返回
},
list: {
type: Array,
default: () => [] // 工厂函数返回
}
}
在父组件没有传递数据时,子组件通过 Props
接收了性但是没有设置默认值的情况下,这个 Prop
的值拿到就会是 undefined
。
但是这种情况对于 布尔类型
除外,如果定义了 Prop
是 布尔类型
,而父组件没有传递,那么这个 Prop
取到的值是 false
,除非显式通过 default
设置其默认值为 undefined
。否则 布尔类型
在这种情况取到的值是 false
。
其目的是为了贴合 HTML5
的规范。
不传:
<my-child></my-child>
<!--等同与-->
<my-child :isOk="false"></my-child>
传:
<my-child isOk></my-child>
<!--等同与-->
<my-child :isOk="true"></my-child>
就好像 input
标签中的 disabled
属性一样,可以简写。
不被 Props 接收的属性
父组件绑定的数据,子组件未定义 Prop
接收,那么这个属性就会挂到子组件的根元素上。这种行为称为 Props透传
。
父组件:
<template>
<div>
<Child msg="Hello,Vue!" class="text-wrap"/>
</div>
</template>
<script>
import Child from "@/components/Child.vue";
export default {
components: {
Child,
},
};
</script>
子组件:
<template>
<div class="child-container">我是子组件</div>
</template>
<script>
export default {};
</script>
子组件没有定义 Prop
接收父组件绑定的值,这些绑定的属性自动挂到子组件的根元素上。并对 Class
和 Style
的冲突做合并。
上面例子中父元素绑定的 class="text-wrap"
会和子组件根节点的 class="child-container"
合并成 class="child-container text-wrap"
。
阻止 Props 透传
如果不希望组件的根节点接受透传属性,那么可以在组件的实例中设置 inheritAttrs: false
。
Class
和Style
不会受到inheritAttrs: false
的影响。
父组件:
<template>
<div>
<Child msg="Hello,Vue!" class="text-wrap"/>
</div>
</template>
<script>
import Child from "@/components/Child.vue";
export default {
components: {
Child,
},
};
</script>
子组件:
<template>
<div class="child-container">我是子组件</div>
</template>
<script>
export default {
// 不接受透传属性,但是 Class 和 Style 不受影响。
inheritAttrs: false,
};
</script>
$attrs
子组件未定义 Prop
接收的属性,都可以在 $attrs
中访问到。
子组件:
<template>
<div class="child-container">我是子组件</div>
</template>
<script>
export default {
inheritAttrs: false,
created() {
console.log(this.$attrs, "$attrs"); // { msg: "Hello,Vue!" }
},
};
</script>
$emit
使用
$emit
它的作用是通过触发实例上的自定义事件,从而向父组件传递消息。父组件可以监听这个事件并做出相应的处理。
$emit
是一个方法,它的第一个参数是 事件名称
,其后面所有传入的参数都会作为向父组件传递的数据。
父元素:
<template>
<div>
<Child @child-change="handleChange"/>
</div>
</template>
<script>
import Child from "@/components/Child.vue";
export default {
components: {
Child,
},
methods: {
handleChange(person, msg) {
console.log(person, "person");
console.log(msg, "msg");
}
}
};
</script>
子组件:
<template>
<div class="child-container">我是子组件</div>
</template>
<script>
export default {
mounted() {
this.$emit("child-change", { name: "张三" }, "Hello,Vue!");
}
};
</script>
事件名
事件名不存在任何自动化的大小写转换。例如,子组件触发了一个小驼峰命名的事件名 myEvent
。
this.$emit('myEvent');
那父元素就必须要监听 myEvent
这个名字的事件。
<Child @myEvent="handleEvent"/>
<!-- 短横线命名(Kebab Case)-->
<Child @my-event="handleEvent"/>
因为事件名不存在任何大小写转换,所以上面例子中的监听到的 my-event
事件不会被触发。
事件名规范更推荐使用 短横线命名(Kebab Case)
,也就是上面例子中的 my-event
的命名方式。