父子组件通信的所有方式?
1、Props + $emit( )
父组件向子组件传递数据
我们在父组件(Parent.vue
)中定义一个 name
数据,通过 v-bind
指令将 name
重命名为 parentName
传递给子组件
<template>
<h1>父组件</h1>
<p>{{ name }}</p>
<Child :parentName="name"></Child>
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child,
},
data() {
return {
name: '周帅帅'
}
}
}
</script>
我们在子组件(Child.vue
)中使用 props
方法接收父组件传递的参数 parentName
,除了接收值为数组外,接收值还可以为对象,并且还可以设置默认值。
<template>
<div>我是子组件</div>
<p>{{ parentName }}</p>
</template>
<script>
export default {
// 还可以定义接收的类型
// props: { parentName: { type: String, default: '' } }
props: ['parentName'],
}
</script>
子组件向父组件传递数据
我们在子组件(Child.vue
)中添加 button
按钮, 点击时我们使用 $emit()
的方式将 handleClick
事件重命名为 childMessage
方法传递给父组件
<template>
<div>我是子组件</div>
<p>{{ parentName }}</p>
<button @click="handleClick">按钮</button>
</template>
<script>
export default {
props: ['parentName'],
methods: {
handleClick() {
this.$emit('childMessage', '子组件的值')
}
}
}
</script>
我们在父组件(Parent.vue
)接收子组件(Child.vue
)传递的 childMessage
方法,并重新命名为 parentClick
方法,并将子组件传递在 $event
的值赋值给 name
,这样我们发现父组件中的 name
值发生了改变。
<template>
<h1>父组件</h1>
<p>{{ name }}</p>
<Child :parentName="name" @parentClick="childMessage"></Child>
<!-- 下面的这行代码和上面代码意思是一样的 -->
<!-- <Child :parentName="name" @childMessage="name = $event"></Child> -->
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child,
},
data() {
return {
name: '周帅帅'
}
},
methods: {
parentClick($event) {
this.name = $event
}
}
}
</script>
2、使用回调函数(callback)方式
我们在父组件(Parent.vue
)中定义一个 changeMessage
来修改 name
的值,然后将 parentClick
方法和 parentName
的值传递给子组件(Child.vue
)
<template>
<h1>父组件</h1>
<div>{{ name }}</div>
<Child :parentName="name" :parentClick="changeMessage"></Child>
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child,
},
data() {
return {
name: '周帅帅'
}
},
methods: {
changeMessage() {
this.name = 'web前端'
}
}
}
</script>
我们在子组件(Child.vue
)通过 props
接收父组件传递的 msg
和 parentClick
方法
<template>
<h1>我是子组件</h1>
<p>{{parentName}}</p>
<button @click="parentClick">按钮</button>
</template>
<script>
export default {
props: ['parentName', parentClick],
}
</script>
这样当我们点击按钮的时候,发现父组件中的 name
和子组件中的 parentName
都会发生改变。
3、$
children + $
parent
我们在父组件(Parent.vue
)通过 this.$children[0].age
获取子组件(Child.vue
)中 age
的值并修改为50
<template>
<h1>父组件</h1>
<p>{{ name }}</p>
<Child @click="changeChildAge">按钮</Child>
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child,
},
data() {
return {
name: '周帅帅'
}
},
methods: {
changeChildAge() {
// 因为子组件可能存在多个,所以加了一个下标0
this.$children[0].age = 50;
}
}
}
</script>
我们在子组件(Child.vue
)通过 this.$parent.message
获取父组件中的值,并在子组件中展示出来
<template>
<div>
<div>我是子组件</div>
<p>{{ age }}</p>
<p>{{ parentName }}</p>
</div>
</template>
<script>
export default {
data() {
return {
age: 10
}
},
computed: {
parentName() {
return this.$parent.name
}
}
}
</script>
所以在子组件(Child.vue
)中可以显示父组件的 name
值,当父组件中的按钮点击时,子组件(Child.vue
)中 age
的值会从10变成50
4、provide + inject
我们在父组件(Parent.vue
)里面通过 provide
提供一个 message
的值
<template>
<h1>父组件</h1>
<Child></Child>
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child,
},
provide: {
name: '周帅帅'
}
}
</script>
子组件(Child.vue
)中通过 inject
接收父组件(Parent.vue
)中传递的值
<template>
<div>
<h1>子组件</h1>
{{ name }}
</div>
</template>
<script>
export default {
inject: ['name']
}
</script>
这样我们可以在子组件(Child.vue
)中看到父组件中定义的 name
值
5、$
attrs + $
listeners
我们在父组件(Parent.vue
)中向子组件(Child.vue
)传递 name
和 age
,我们在父组件(Parent.vue
)中定义了一个 parentName
事件监听器
<template>
<h1>父组件</h1>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<Child :name="name" :age="age" @parentName="changeName"></Child>
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child,
},
data() {
return {
name: '周帅帅',
age: 24
}
},
methods: {
changeName() {
this.name = '小夏'
}
}
}
</script>
我们在子组件(Child.vue
)中可以通过 $listeners
获取到父组件(Parent.vue
)中的 parentName
,这样我们就能执行父组件(Parent.vue
)中绑定的事件处理函数(changeName
)。然后我们在子组件(Child.vue
)里面引入孙子组件(GrandChild.vue
),将父组件(Parent.vue
)传递给子组件(Child.vue
)的 name
和 age
通过 v-bind="$attrs"
传递给孙子组件(GrandChild.vue
)。
<template>
<div>
<h1>子组件</h1>
<button @click="$listeners.parentName">按钮</button>
// $attrs相当于子组件向孙子组件传递:name="name" :age="age"
<GrandChild v-bind="$attrs"></Child>
</div>
</template>
<script>
import GrandChild from './GrandChild.vue';
export default {
components: {
GrandChild,
},
data() {
return {
}
}
}
</script>
这样我们在孙子组件(GrandChild.vue
)中就可以接收子组件(Child.vue
)传递过来的 name
和 age
了。然后通过 $attrs.name
和 $attrs.age
来使用。
<template>
<div>
<h1>孙子组件</h1>
<p>姓名:{{ $attrs.name }}</p>
<p>年龄:{{ $attrs.age }}</p>
</div>
</template>
<script>
export default {
data() {
return {
// 如果不加这个属性,我们发现div中会携带name和age这两个属性
inheritAttrs: false
}
}
}
</script>
6、ref
我们在父组件(Parent.vue
)中给引入子组件(Child.vue
),并且给子组件(Child.vue
)中定义一个 ref
为childName
的值。
<template>
<h1>父组件</h1>
<Child ref="childName"></Child>
<button @click="changeName">按钮</button>
</template>
<script>
import Child from './Child.vue';
export default {
components: {
Child,
},
methods: {
changeName() {
console.log('修改前', this.$refs.childName.name)
this.$refs.childName.changeName()
console.log('修改后', this.$refs.childName.name)
}
}
}
</script>
我们在子组件(Child.vue
)中定义一个 changeName
的方法
<template>
<div>
<h1>子组件</h1>
</div>
</template>
<script>
export default {
data() {
return {
name: '周帅帅'
}
},
methods: {
changeName() {
this.name = '前端程序员'
}
}
}
</script>
这样当我们点击父组件(Parent.vue
)中的按钮时,我们在浏览器的控制台就可以看到打印的值发生了变化。
几种父子传递方法的区别
props
props
只能用于父组件传递给子组件的时候区使用。
provide与inject?
我们使用 provide
和 inject
进行传值可以在父子组件中传递,也可以将父子组件的传递的值在孙子组件中接收,例子在下面。
举个例子
父组件(Parent.vue
)里调用了子组件(Child.vue
),同时子组件(Child.vue
)里调用了孙子组件(GrandChild.vue
)
区别
父组件(Parent.vue
)可以用 props
方法直接传数据给子组件(Child.vue
),但不能传数据给孙子组件(GrandChild.vue
)
父组件(Parent.vue
)可以用 provide
与 inject
方法直接传递数据给子组件(Child.vue
),也可以直接给孙子组件(GrandChild.vue
)
官方说明provide和inject
- 类型:
provide:Object | () => Object
inject:Array<string> | { [key: string]: string | Symbol | Object }
- 详细:这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
provide
选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的property
。在该对象中你可以使用ES2015 Symbols
作为key
,但是只在原生支持Symbol
和Reflect.ownKeys
的环境下可工作。inject
选项应该是:- 一个字符串数组,或
- 一个对象,对象的
key
是本地的绑定名,value
是:- 在可用的注入内容中搜索用的
key
(字符串或Symbol
),或 - 一个对象,该对象的:
from property
是在可用的注入内容中搜索用的key
(字符串或Symbol
)default property
是降级情况下使用的value
- 在可用的注入内容中搜索用的
提示
provide
和 inject
绑定并不是可响应的。这是刻意为之的也就是说当我们修改子组件中的值父组件的值不会跟着改变。然而,如果你传入了一个可监听的对象,那么其对象的 property
还是可响应的。
案例:
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
利用 ES2015 Symbols
、函数 provide
和对象 inject
:
const s = Symbol()
const Provider = {
provide () {
return {
[s]: 'foo'
}
}
}
const Child = {
inject: { s },
// ...
}
接下来 2 个例子只工作在 Vue 2.2.1
或更高版本。低于这个版本时,注入的值会在 props
和 data
初始化之后得到。
使用一个注入的值作为一个 property
的默认值:
const Child = {
inject: ['foo'],
props: {
bar: {
default () {
return this.foo
}
}
}
}
使用一个注入的值作为数据入口:
const Child = {
inject: ['foo'],
data () {
return {d
bar: this.foo
}
}
}
在 2.5.0+
的注入可以通过设置默认值使其变成可选项:
const Child = {
inject: {
foo: { default: 'foo' }
}
}
如果它需要从一个不同名字的 property
注入,则使用 from
来表示其源 property
:
const Child = {
inject: {
foo: {
from: 'bar',
default: 'foo'
}
}
}
与 prop
的默认值类似,你需要对非原始值使用一个工厂方法:
const Child = {
inject: {
foo: {
from: 'bar',
default: () => [1, 2, 3]
}
}
}
$
attrs与$
listeners?
$
attrs
包含了父作用域中不作为 prop
被识别 (且获取) 的 attribute
绑定 (class
和 style
除外)。当一个组件没有声明任何 prop
时,这里会包含所有父作用域的绑定 (class
和 style
除外),并且可以通过 v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。我们需要注意的是 inheritAttrs
属性,当我们设置这个属性为 inheritAttrs: false
时,就可以避免父作用域中的 $attrs
上的属性绑定到子组件的根元素上。
$
listeners
包含了父作用域中的 (不含 .native
修饰器的) v-on
事件监听器。它可以通过 v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用。
$parent
和 $children
和 props
的区别?
props
仅仅只能接收,单项绑定。着重数据传递。向子组件传递数据。$parent
既可以接受父组件数据 ,又可以修改父组件数据,是双向的 。$parent
还可以调用父组件的方法。
$
children的缺点?
由于在一个 vue
实例中,它的子组件个数会随着需求的增多而增多,而我们在父组件想要获得某个子组件的数据或方法,是通过 this.$ children[ ]
的具体下标获取的。那么当我们在这个子组件前面再添加其它子组件,我们想要获得的子组件下标就不再是上次我们写的那个,这使得我们写死的下标具有不确定性。我们可以通过 $refs
解决这个问题,因为this.$refs
是一个对象类型,它包含所有子组件对象。