一、通过 props 传递
子组件设置props属性,定义接收父组件传递过来的参数
父组件在使用子组件标签中通过字面量来传递值
# 父组件
<Children name="Mike" age=18 />
# 子组件
props: {
// 字符串形式
name: String // 接收的类型参数
// 对象形式
age:{
type: Number, // 接收的类型
defaule: 18, // 默认值为
require: true // 属性必须传递
}
}
二、通过 $emit 触发自定义事件
子组件通过$emit触发自定义事件,$emit第二个参数为传递的数值
父组件绑定监听器获取到子组件传递过来的参数
# 父组件
<Children @add="cartAdd($event)" />
# 子组件
this.$emit('add', 'xxx')
三、使用 ref 或 $children
1、ref
ref属性可以给任何组件或元素赋一个唯一的标识符,通过这个标识符可以直接访问对应的组件或元素。
注意:$refs属性是一个对象,它的属性就是各个ref属性的标识符,属性值则对应着实际的组件或元素。
相对于$children属性,$refs属性更加灵活和便于使用,因为可以给任何组件或元素添加ref属性,并且不需要知道索引值。但是,需要注意避免命名冲突。
# 父组件
<Children ref="childeren" />
this.$refs.children // 获取子组件实例,通过子组件实例就能调用子组件自身的属性和方法
2、$children
$children属性是一个数组,包含了当前组件中所有的直接子组件。
注意:$children属性只包括直接子组件,而不包括孙子组件或更深层次的组件。如果想要访问所有的子孙组件,可以使用递归的方式来遍历整个组件树。
<template>
<div>
<child-component ref="child"></child-component>
</div>
</template>
<script>
import ChildComponent from '@/path/ChildComponent.vue'
export default {
name: 'ParentComponent',
components: {
ChildComponent
},
mounted() {
console.log(this.$children[0]) // 访问第一个子组件实例
console.log(this.$refs.child) // 通过ref属性访问子组件实例
}
}
</script>
四、EventBus
创建一个中央时间总线EventBus
兄弟组件通过$emit触发自定义事件,$emit第二个参数为传递的数值
另一个兄弟组件通过$on监听自定义事件
// 创建一个中央时间总线类
class Bus {
constructor() {
this.callbacks = {}; // 存放事件的名字
}
$on(name, fn) {
this.callbacks[name] = this.callbacks[name] || [];
this.callbacks[name].push(fn);
}
$emit(name, args) {
if (this.callbacks[name]) {
this.callbacks[name].forEach((cb) => cb(args));
}
}
}
# main.js
Vue.prototype.$bus = new Bus() // 将$bus挂载到vue实例的原型上
// 另一种方式
Vue.prototype.$bus = new Vue() // Vue已经实现了Bus的功能
# 子组件1
this.$bus.$emit('foo')
# 子组件2
this.$bus.$on('foo', this.handle)
五、$parent 或 $root
通过共同祖辈$parent 或者 $root搭建通信
# 父组件
<div>
<!-- 子组件1 -->
<Children1 />
<!-- 子组件2 -->
<Children2 />
</div>
# 子组件1
this.$parent.on('add', this.add);
或者
this.$root.on('add', this.add)
# 子组件2
this.$parent.emit('add', '传输数据');
或者
this.$root.emit('add', '传输数据');
六、$attrs 与 $listeners
$attrs:继承所有的父组件属性(除了 prop 传递的属性、class 和 style ),一般用在子组件的子元素上,可以通过 v-bind="$attrs" 传⼊。
$listeners:是一个对象,里面包含了作用在这个组件上的所有监听器,可以配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。(相当于子组件继承父组件的事件)。
1、给子组件传值
# 父组件
<Children foo="foo" />
# 子组件
# 子组件并未在props中声明foo
<p>{{ $attrs.foo }}</p>
2、给孙组件传值
# 父组件
<Children msg="xxx" @some-event="onSomeEvent" @some-event2="onEvent2" />
# 子组件
<Grandson v-bind="$attrs" v-on="$listeners" />
this.$emit('some-event2', '传输的数据')
# 孙组件
<div @click="$emit('some-event', '传输的数据')">
{{ msg }}
</div>
七、Provide 与 Inject
在祖先组件定义provide属性,返回传递的值或方法
在后代组件通过inject接收组件传递过来的值或方法
1、vue2 中的用法
默认情况下,provide/inject
绑定并不是响应式的。如果我们想对祖先组件中的更改做出响应,我们需要将 provide 传值进行改变,传入一个响应式对象。
# 祖先组件
provide(){
return {
obj: this.obj, // 传入一个响应式对象
changeVal: this.changeName // 传入一个方法
}
},
data(){
return {
obj: {
name: "leo"
}
}
},
methods:{
changeName(val) { // 触发该方法执行
this.obj.name = val
}
}
# 后代组件
<template>
<div>
<span>{{ obj.name }}</span>
</div>
</template>
<script>
export default {
inject:[
"obj", // 接收一个响应式对象
"changeVal" // 接收一个方法
],
methods:{
changeName(){
this.changeVal("lion")
}
}
};
</script>
2、vue3 中的用法
如果是在vue3中,则我们可以通过传递一个 ref
property 或 reactive
对象给 provide
来改变这种行为。
1. provide
在 setup()
中使用 provide
时,我们首先从 vue
显式导入 provide
方法。这使我们能够调用 provide
来定义每个 property。
provide
函数允许你通过两个参数定义 property:
- name (
<String>
类型) - value
2. inject
在 setup()
中使用 inject
时,也需要从 vue
显式导入。导入以后,我们就可以调用它来定义暴露给我们的组件方式。
inject
函数有两个参数:
- 要 inject 的 property 的 name
- 默认值 (可选)
为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 或 reactive。
# 父组件
<template>
<Children />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import Children from './Children.vue'
export default {
components: {
Children
},
setup() {
const location = ref('North Pole');
const geolocation = reactive({
longitude: 90,
latitude: 135
})
// 修改方法
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', location)
provide('geolocation', geolocation)
provide('updateLocation', updateLocation) // 传入一个方法
}
}
</script>
# 子组件
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
const updateUserLocation = inject('updateLocation')
return {
userLocation,
userGeolocation,
updateUserLocation // 执行该方法,触发祖先组件方法执行,从而改变数据
}
}
}
</script>
如果要确保通过 provide
传递的数据不会被 inject 的组件更改,我们建议对提供者的 property 使用 readonly
<template>
<Children />
</template>
<script>
import { provide, reactive, readonly, ref } from 'vue';
import Children from './Children.vue';
export default {
components: {
Children
},
setup() {
const location = ref('North Pole');
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = 'South Pole'
}
// 使用readonly,数据只读
provide('location', readonly(location))
provide('geolocation', readonly(geolocation))
provide('updateLocation', updateLocation)
}
}
</script>
八、Vuex
复杂关系的组件数据传递可以通过vuex存放共享的变量。
Vuex作用相当于一个用来存储共享变量的容器
- state用来存放共享变量的地方
- getter,可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值
- mutations用来存放修改state的方法。
- actions也是用来存放修改state的方法,不过action是在mutations的基础上进行。常用来做一些异步操作