VUE父子组件之间通信方法
1、介绍
1.1 组件
- 组件是可复用的VUE实例,带有一个自己的组件名字,组件可以被用来当做自定义元素开使用。
- 组件有自己独立的作用域,任何数据不会传递到组件里
1.2 组件之间的关系
- 首先介绍VUE框架思想:通常一个应用会以一棵嵌套的组件树的形式来组织;
- 如下图,这是VUE官网介绍组件的图;
- 图中我用红色字母所标出来的:(以A、B、C、E为例)
- B、C和A, E和C 为父子关系;
- B和C是兄弟关系
- A和E是隔代关系
- 但是免不了组件之间需要传递数据来实现一些功能;
- 因为组件有自己的独立的作用域,不能直接访问父组件的数据,因此要实现组件之间的通信,需要使用一些特殊方法。
2、方法一(prop和$emit):
2.1 父传子——使用 v-bind 来动态传递prop
- 父组件
Parent.vue
- 引入了两次子组件:
- 第一次 直接给
hh
属性赋值,将我来啦
传递给子组件,hh
是我自己定义的,可以为任意字符,只要和定义的子组件props
中的属性名相同就行 - 第二次 在使用子组件
<Child1>
的时候,父组件将自己data
里的数据title
使用v-bind
指令绑定在子组件上; hh
可以当做一个属性名,可以为任意值,由你定义;
<template>
<div>
<Child1 hh="我来啦"></Child1>
<Child1 v-bind:hh="title"></Child1>
</div>
</template>
<script>
import Child1 from '../BlogComponentCode/Child/Child1'
export default {
name: "Parent",
components:{
Child1
},
data(){
return{
title: '嘿嘿嘿,我是父组件!'
}
}
}
</script>
- 子组件
Child1.vue
hh
数据就是父组件将自己的数据title
绑定到hh
属性中,- 子组件这边将父组件那边定义的
hh
放进自己的props
列表中,就可以在页面上使用hh
数据了
<template>
<div>
<h2>我是子组件,我收到的值为:{{hh}}</h2>
</div>
</template>
<script>
export default {
name: "Child1",
props:['hh']
}
</script>
- 运行后如下:
- 引入两次子组件
2.2 子传父——使用$emit方法
- 子组件
Child1.vue
- 子组件的
button
按钮使用点击事件click
,其中使用 `$emit`` 方法第一个参数传入父组件定义的传过来的事件名称,第二个参数为要传给父组件的值
<template>
<div>
<button v-on:click="$emit('zichuanfu', content)">1111</button>
</div>
</template>
<script>
export default {
name: "Child1",
data(){
return {
content: '$$$我想吃饭,我来自子组件---'
}
}
}
</script>
- 父组件
Parent.vue
- 在子组件上定义事件
zichuanfu
,通过$event
访问到被传出的值,此处将值赋值给num
,之后用插值表达式渲染到页面上 zichuanfu
可以自定义任意值,与子组件对应就行
<template>
<div>
<Child1 v-on:zichuanfu="num = $event"></Child1>
{{num}}
</div>
</template>
<script>
import Child1 from '../BlogComponentCode/Child/Child1'
export default {
name: "Parent",
components:{
Child1
},
data(){
return{
num:''
}
}
}
</script>
- 运行点击
1111
按钮为:
- 父组件也可以将事件处理函数写成一个方法:
<template>
<div>
<Child1 v-on:zichuanfu="getChild"></Child1>
</div>
</template>
<script>
import Child1 from '../BlogComponentCode/Child/Child1'
export default {
name: "Parent",
components:{
Child1
},
methods:{
getChild(e){
alert(e);
}
}
}
</script>
- 点击按钮运行为:
3、方法二(使用插槽slot传值)
- 这里不使用已废弃的
slot-scope
语法 父级模板里的所有内容都是在父级作用域中编译的
子模板里的所有内容都是在子作用域中编译的
3.1 介绍插槽
- 需要向一个组件传递内容时使用插槽
- 在子组件中定义插槽,可以显示子组件原本的默认内容。如下:
- 子组件
Child1.vue
<template>
<div>
<slot>{{ user.num }}</slot>
</div>
</template>
<script>
export default {
name: "Child1",
data(){
return {
user: {
num: 666,
name: 'ly'
}
}
}
}
</script>
- 父组件
Parent.vue
<template>
<div>
<Child1></Child1>
</div>
</template>
<script>
import Child1 from '../BlogComponentCode/Child/Child1'
export default {
name: "Parent",
components:{
Child1
}
}
</script>
-
因为父组件使用子组件时在模板内没有写任何内容,因此运行显示为子组件的插槽默认内容
-
运行如下:
-
父组件使用子组件时在模板内编写内容,运行时父组件编写的内容会替换子组件的默认内容
-
父组件
Parent.vue
若这样写(子组件不变):
<template>
<div>
<Child1>红红火火恍恍惚惚</Child1>
</div>
</template>
<script>
import Child1 from '../BlogComponentCode/Child/Child1'
export default {
name: "Parent",
components:{
Child1
}
}
</script>
- 运行结果为:
3.2 子传父——使用slot传值
- 但是如果父组件想要显示子组件
data
数据里user.name
呢,因为user.name
在子组件的作用域,父组件无法访问,因此需要使用slot
来传值给父组件了。 - 子组件
Child1.vue
- 要想使父组件能够使用
user
数据,如下列代码,将user
作为slot
的属性绑定上去
<template>
<div>
<slot v-bind:user="user">
{{user.num}}
</slot>
</div>
</template>
<script>
export default {
name: "Child1",
data(){
return {
user: {
num: 666,
name: 'ly'
}
}
}
}
</script>
- 父组件
Parent.vue
- 在父组件这里使用
v-slot
可以定义插槽过来的值的名字,这里我定义为fromChild
,可以为任意值。 default
可以理解为插槽的名字,因为子组件代码里slot
没有定义name
属性,因此这里为default
;如果插槽定义了name
属性,则这里的default
更改为name
的属性值user
使用fromChild
值可以点出来,可以拿到name
值,这样就可以实现在父组件这边将子组件原本的user.num
更换为user.name
- 将
user.name
也可以赋值给父组件的data
里的值numName
,就可以拿过来直接使用了。
<template>
<div>
<Child1>
<template v-slot:default="fromChild">
{{ numName = fromChild.user.name}}
</template>
</Child1>
<hr>
{{numName + '888'}}
</div>
</template>
<script>
import Child1 from '../BlogComponentCode/Child/Child1'
export default {
name: "Parent",
components:{
Child1
},
data(){
return{
numName:''
}
}
}
</script>
- 运行如下:
4、方法三($parent 和 ref)
4.1 子传父——使用ref和 $refs
- 直接访问一个子组件使用
$ref、$refs
- 注意!!
$refs
只会在组件渲染完成之后生效,并且非响应式!(避免在模板或计算属性中使用 $refs) - 父组件
Parent.vue
- 以下为在父组件中直接访问子组件中的data值
- 在子组件上使用
ref
属性为子组件设置一个名称ref="child"
,child
可为任意值, - 以下代码表示点击
button
按钮,出弹框显示子组件data
中user.name
的值 - 根据设置的名称
child
获取子组件引用this.$refs.child
,接下来可以直接获取子组件的数据或方法了
<template>
<div>
<Child1 ref="child"></Child1>
<button @click="getChildData">dianwo</button>
</div>
</template>
<script>
import Child1 from '../BlogComponentCode/Child/Child1'
export default {
name: "Parent",
components:{
Child1
},
methods:{
getChildData(){
alert(this.$refs.child.user.name);
}
}
}
</script>
- 子组件
Child1.vue
<template>
<div>
<h4>我是子组件</h4>
</div>
</template>
<script>
export default {
name: "Child1",
data(){
return {
user: {
num: 666,
name: 'ly'
}
}
}
}
</script>
- 点击按钮
dianwo
运行如下:
4.2 父传子——使用$parent
- 要直接访问父组件,可使用
$parent
直接访问父组件的实例 - 子组件
Child1.vue
<template>
<div>
<div>{{$parent.$data.title}}</div>
</div>
</template>
<script>
export default {
name: "Child1"
}
</script>
- 父组件
Parent.vue
<template>
<div>
<child1></child1>
</div>
</template>
<script>
import Child1 from '../BlogComponentCode/Child/Child1'
export default {
name: "Parent",
components:{
Child1
},
data(){
return{
title: '嘿嘿嘿,我是父组件!'
}
}
}
</script>
- 运行如下:
5、方法四(provide 和 inject)
5.1 父传子——使用provide 和 inject
- 方法三中的
$parent
有一个明显的缺点,就是不能访问更深层级的实例上,除非this.$parent.$parent...
,但是这无疑是非常麻烦的,因此下面介绍新的方法: - 首先这个方法用到了两个新的实例选项:
provide
和inject
provide
可以在父组件上指定需要提供给后代组件
的数据或者方法(注意是后代组件)inject
可以在子组件上用来接收provide
提供的所需要的数据或者方法!!!此方法非响应式
- 上代码啦!
- 父组件
Parent.vue
- 以下代码中
provide
选项将此组件的sendName函数
和title数据
提供给后代,注意:provide
选项中的getSendName
和getTitle
是提供的数据的名称,可以为任意名称
<template>
<div>
<child1></child1>
</div>
</template>
<script>
import Child1 from '../BlogComponentCode/Child/Child1'
export default {
name: "Parent",
components:{
Child1
},
data(){
return{
title: '嘿嘿嘿,我是父组件!'
}
},
provide: function(){
return {
getSendName: this.sendName,
getTitle: this.title
}
},
methods:{
sendName(){
alert("11111");
}
}
}
</script>
- 子组件
Child1.vue
- 使用
inject
引入祖先组件
中提供的provide
中此组件所需的数据,就可以直接使用了
<template>
<div>
<button @click="getSendName">{{getTitle}}</button>
</div>
</template>
<script>
export default {
name: "Child1",
inject: ['getSendName', 'getTitle']
}
</script>
- 运行如下