接上一篇的基础继续
父子组件的通信
方法:
(1)通过props
属性向子组件传递数据
(2)通过$emit()
来自定义事件event
向父组件传递数据
props --properties–property的复数
以下直接用Vue实例(app)模拟父组件,一般组件(cpn)模拟子组件。
1.Props 子传父
(1)简单举例
主要是利用组件的props。
Prop
是你可以在组件上注册的一些自定义attribute
。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个property
。
<div id="app">
<cpn v-bind:c_ahs="ahs">{{ahs}}</cpn>
</div>
<template id="cpn">
<div>
<p>{{c_ahs}}</p>
</div>
</template>
在子组件模板中写入组件props
中的数据,在使用时使用v-bind
绑定子组件定义的数据和父组件的数据。
<script>
const cpn = {
template:`#cpn`,
props:['c_ahs'], //!!!在子组件中把‘c_ahs’加入props
data(){
return{}
}
};
const app = new Vue({
el:'#app',
data:{
ahs:'okok'
},
components:{
cpn
}
})
</script>
结果:
(2)Prop的类型
实际上任何类型的值都可以传给一个 prop
。
上面的简单示例中,我们传入了数组类型。
通常你希望每个 prop
都有指定的值类型。这时,你可以以对象形式列出 prop
,这些 property 的名称和值分别是 prop
各自的名称和类型:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
使用上面这种形式可以实现类型限制。
>>类型限制
<script>
const cpn = {
template:`#cpn`,
//props:['c_ahs'],
props:{
c_ahs:Boolean //当在这里限制c_ahs为布尔值时,而与之绑定的ahs是字符串时
},
data(){
return{}
}
};
</script>
当在这里限制c_ahs为布尔值时,而与之绑定的ahs是字符串时,浏览器就会报错。结果如下图:
null
和undefined
会通过任何类型验证
>>提供默认值
通过prop的这类型,也可以为组件提供一些默认值。
<div id="app">
<!-- <cpn v-bind:c_ahs="ahs">{{ahs}}</cpn>-->
<cpn></cpn>
</div>
const cpn = {
template:`#cpn`,
//props:['c_ahs'],
props:{
c_ahs:{
type:String,
default:'HelloWorld', //默认值
required:true //必须传值
}
},
data(){
return{}
}
};
我们在HTML中使用组件cpn,没有进行绑定传值。结果如下(出现默认值):
另外,当默认类型为array
或者object
时,一般写法为:
props:{
c_ahs:{
type:Array,
default:[] //默认值
}
}
但这种写法在Vue 2.5.17
以上的版本不能使用,浏览器会报错。要求array
和object
的默认值必须为一个function
。
写法如下:
props:{
c_ahs:{
type:Array,
default(){
return[]
} //默认值
}
}
(3)单向数据流
这里提到Vue
的一个重要概念!
单向数据流
:`
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
但是:
注意在 JavaScript 中对象和数组是通过引用传入的,
所以对于一个数组或对象类型的 prop来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
(4)Props的驼峰标识
当我们在props
中用到驼峰时,此时组件的标签中的v-bind就不能写成驼峰式。因为v-bind只能不能识别大写字母。
为了避免这种情况发生,我们尽量少用驼峰格式,如果迫不得已的话,我们可以在用v-bind的时候稍微修改一下。
<div id="app">
<cpn v-bind:my-message=""></cpn>
</div>
<script>
const cpn = {
template: '#cpn',
props: {
myMessage: {
type: Boolean
}
}
</script>
2.$emit()
父传子
自定义事件$emit()
this.$emit
(‘自定义事件名’,要传送的数据);
触发当前实例上的事件,要传递的数据会传给监听器;
主要步骤:
- 在子组件中,通过$emit()来触发事件;
- 在父组件中,通过v-on来监听子组件事件。
(v-on不仅仅可以监听DOM事件,也可以用于组件间的自定义事件。)
<!--父组件模板-->
<div id="app">
<cpn v-on:itemclick="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<button v-for="item in categories"
@click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
<script>
//子组件
const cpn = {
template:`#cpn`,
data(){
return{
categories:[
{id:'aaa',name:'藤原千花'},
{id:'bbb',name:'石上优'},
{id:'ccc',name:'白银御行'},
{id:'ddd',name:'四宫辉夜'},
]
}
},
methods:{
btnClick(item){
//子组件发射事件
this.$emit('itemclick')
}
}
};
//父组件
const app = new Vue({
el:"#app",
data:{},
components:{
cpn
},
methods: {
//父组件接受事件
cpnClick(){
console.log('cpnClick')
}
}
});
</script>
结果如下:
再略加上一点点东西:
//子组件部分
methods:{
btnClick(item){
//子组件发射事件
this.$emit('itemclick',item)
}
}
//父组件部分
methods: {
//父组件接受事件
cpnClick(item){
console.log('cpnClick',item)
}
}
则结果如下: