这是我参与「第四届青训营 」笔记创作活动的的第5天
父子组件通信
子组件是不能引用父组件或者ue实例的数据的。但是,在开发中,往往一些数据确实需要从上层传递到下层︰ 比如在一个页面中,我们从服务器请求到了很多的数据。 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
如何进行父子组件间的通信呢?
Vue官方提到
- 通过props向子组件传递数据 (properties--属性)
- 通过事件向父组件发送消息
在下面的代码中,我直接将Vue实例当做父组件,并且其中包含子组件来简化代码。
父传子--props
props基本用法
在组件中,使用选项props来声明需要从父级接收到的数据。props的值有两种方式:
- 方式一︰ 字符串数组,数组中的字符串就是传递时的名称。
- 方式二︰ 对象,对象可以设置传递时的类型,也可以设置默认值等。
子组件使用props数组对象--接收父组件中的数据,然后在子组件中使用props数组对象的对象
除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。 验证都支持哪些数据类型呢?
String
Number
Boolean
Array
Object
Date
Function
Symbol
当我们有自定义构造函数时,验证也支持自定义的类型
<!-- <script>
Vue.component('mycpncc',{
props : {
// 基础的类型检查( `null`匹配任何类型)
propA: Number,
// 多个可能的类型
propB: [string, Number],
// 必填的字符串
propc: {
type: string,
required: true
},
//带有默认值的数字
propD: {
type: Number,
default: 100
},
//带有默认值的对象
propE: {
type: object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return {
message : 'he11o'
}}
},
//自定义验证函数
propF: {
validator: function (va1ue) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexof(value) !== -1
}
}
}})
function Person (firstName,lastName) {
this.firstName = firstName
this.lastName = lastName
}
Vue.component('blog-post',{
props:{
author : Person
}
})
</script> -->
// 父传子
<div id='a'>
<son1 v-bind:cmovies="movies" :cmessege="messege"></son1>
<!-- <son1 :cmovies="movies" ></son1> -->
<!-- <son1 :cmessege="messege"></son1> -->
</div>
<template id="son1">
<ul>
<li v-for="item in cmovies">{{item}}</li>
<p>{{cmessege}}</p>
</ul>
</template>
<script src='./vue.js'></script>
<script>
const son1 = {
template : '#son1',
// 方式一︰ 字符串数组
// props : ['cmovies','cmessege']
// 方式二︰ 对象,对象可以设置传递时的类型,也可以设置默认值等。
props : {
// 1. 类型限制
// cmovies : Array,
// cmessege : String
// 2. 设置默认值,以及必传值
// 类型是 Object/Array 时,默认值必须是一个函数
cmessege : {
type : String,
default : '我是默认值',
required : true // 布尔值,必须传
},
cmovies : {
type : Array,
// defalut : [], // vue2.5.7以下可以用
default (){
return ['默认值']
}
}
}
}
const a = new Vue({
el : '#a',
data : {
messege : '父组件传来的数据',
movies : ['怦然心动','泰坦尼克号','闻香识女人','贱女孩']
},
components : {
// son1 :son1
son1
}
})
</script>
props驼峰标识
如果props使用的驼峰标识,在动态绑定的时候,不能直接绑定,在每个大写字母前面需要加一个- 大写改为小写
因为:html属性不区分大小写 ??
v-bind不支持驼峰命名,在子组件模板、props 中可正常使用驼峰命名, 父组件向子组件传数据时,动态绑定时不能用驼峰命名,cMessege = c-messege
模板中如果有多个标签,必须包裹在一个div里
<div id='b'>
<son2 :c-info="info" :c-my-messege="messege"></son2>
</div>
<template id="son2">
<div>
<h3>{{cInfo}}</h3>
<h3>{{cMyMessege}}</h3>
</div>
</template>
<script src='./vue.js'></script>
<script>
const son2 = {
template : '#son2',
props : {
cInfo : {
type : Object,
default (){
return {}
}
},
cMyMessege : {
type : String,
default : '默认值'
}
}
}
const b = new Vue({
el : '#b',
data : {
info : {
name1 : 'sonny',
name2 : 'alban'
},
messege : 'hello'
},
components :{
son2
}
})
</script>
子传父--自定义事件
当子组件中发生事件的时候,通过 $emit 发射自定义事件,父组件通过v-on监听自定义事件
父组件函数的默认参数为$emit传入的参数
like @click="btnClick"没有写参数,默认第一个参数为event
当子组件需要向父组件传递数据或事件时,就要用到自定义事件了。
我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。
自定义事件的流程∶
在子组件中,通过$emit()来触发事件。
在父组件中,通过v-on来监听子组件事件。
我们来看一个简单的例子︰
我们之前做过一个两个按钮+1和-1,点击后修改counter。 我们整个操作的过程还是在子组件中完成,但是之后的展示交给父组件 这样,我们就需要将子组件中的counter,传给父组件的某个属性,比如total,。
<div id='c'>
<son3 @item-click="aClick"></son3>
</div>
<template id="son3">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<script src='./vue.js'></script>
<script>
const son3 = {
template : '#son3',
data(){
return {
categories: [
{id : 1, name : '热门推荐'},
{id : 2, name : '热映电影'},
{id : 3, name : '经典老片'}
]
}
},
methods : {
btnClick(item){
// 发射事件 自定义事件
// '事件名称'--父组件监听的事件
this.$emit('item-click',item)
}
}
}
const c = new Vue({
el : '#c',
data : {
},
components : {
son3
},
methods : {
aClick(item){
console.log('此时浏览:',item.name);
}
}
})
</script>
父子组件的访问方式
在父组件中能直接拿到子组件的对象,对其进行操作 or 直接调用子组件的方法
有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。
- 父组件访问子组件:使用 refs reference(引用)
- 子组件访问父组件:使用 $parent
父访问子:$children
this.$children -- 数组类型,它包含所有子组件对象。通过 this.$children[]来访问
$children的缺陷:
通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
<div id='a'>
<cpn1></cpn1>
<cpn1></cpn1>
<cpn1></cpn1>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn1">
<div>
我是子组件
</div>
</template>
<script src='./vue.js'></script>
<script>
const a = new Vue({
el : '#a',
data : {
},
methods : {
btnClick (){
// console.log(this.$children);
console.log(this.$children[2].messege);
this.$children[2].showMessege()
// this.$children是一个数组类型,它包含所有子组件对象。
// 如果后期插入了新的组件,会导致数组改变,出现问题
}
},
components : {
cpn1 : {
template : '#cpn1',
data(){
return {
messege : '我是子组件的数据'
}
},
methods : {
showMessege (){
console.log('我是自组件的方法');
}
}
}
}
})
</script>
父访问子:$refs
this.$refs -- 对象类型,需要通过给子组件绑定特定的ID,通过 this.$refs.ID 来访问
我们想明确获取其中一个特定的组件时,可以使用$refs
$refs的使用:
$refs和ref指令通常是一起使用的。
首先,我们通过 ref 给某一个子组件绑定一个 特定的ID。
其次,通过 this.$refs.ID 就可以访问到该组件了。
<div id='b'>
<h2>从子组件拿来的数据{{cdata}}</h2>
<cpn2 ref="aaa"></cpn2>
<cpn2></cpn2>
<cpn2 ref="aaa"></cpn2>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn2">
<div>
我是子组件
</div>
</template>
<script src='./vue.js'></script>
<script>
const b = new Vue({
el : '#b',
data : {
cdata : ''
},
methods : {
btnClick (){
console.log(this.$refs.aaa._uid);
this.$refs.aaa.showMessege()
console.log(this.$refs.aaa.messege);
this.cdata = this.$refs.aaa.messege
}
},
components : {
cpn2 : {
template : '#cpn2',
data(){
return {
messege : '我是子组件的数据',
}
},
methods : {
showMessege (){
console.log('我是子组件的showMessege方法');
}
}
}
}
})
</script>
访问:父获取子的值->父在主动, 通信:父获取子的值->子在主动
children是可以拿到所有的组件里面的值,ref是可以每次都精准的拿到一个数组里面的值
ref相同,后者覆盖前者,注册制最新的变了,$refs.属性名返回的是一个数组了。。
子访问父:$parent
如果我们想在子组件中直接访问父组件,可以通过$parent
注意事项:
尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中 尽量 不要这样做。
子组件应该 尽量避免 直接访问 父组件的数据,因为这样 耦合度太高了。
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我们的调试和维护。
开发不建议用 这样使用,子组件就不够独立,复用性不强,可能另一个父组件中没有对应的属性,会引起问题,耦合度太高
访问根组件:$root
<div id='c'>
<h3>我是根组件</h3>
<cpn3 ></cpn3>
</div>
<template id="cpn3">
<div>
<h3>我是父组件</h3>
<ccpn3></ccpn3>
</div>
</template>
<template id="ccpn3">
<div>
<p>我是子组件</p>
<button @click="btnClick">按钮</button>
</div>
</template>
<script src='./vue.js'></script>
<script>
const c = new Vue({
el : '#c',
data : {
messege : '我是根组件的数据'
},
methods : {
},
components : {
cpn3 : {
template : '#cpn3',
data(){
return {
messege : '我是父组件的数据'
}
},
methods : {
showMessege (){
console.log('我是父组件的showMessege方法');
}
},
components : {
ccpn3 : {
template : '#ccpn3',
data(){
return {
cmessege : '我是子组件的数据'
}
},
methods : {
btnClick (){
this.$parent.showMessege()
console.log(this.$parent.messege)
console.log(this.$root.messege);
console.log(this.$parent.$parent.messege);
console.log(this.$root);
},
}
}
}
}
}
})
</script>