目录
1.props父向子组件通信
2.自定义事件 子向父组件通信
3.全局事件总线
4.v-model组件通信(父子组件数据同步)
绑定单个数据同步
绑定多个数据同步
5.useAttrs组件通信
6.ref与$parent
ref获取子组件实例对象
$parent获取父组件实例对象
7.provide-inject 可以实现隔辈传输
8.Pinia
选择式API:
组合式API:
我们用Vue3开发项目时,常常需要面对的一个问题就是组件之间的通信,如何将数据发给对应的组件,这是不可避免的一个问题,该篇讲述了Vue3的8大主要通信方式。(还有其他的可以补充)
父组件:
props用于父组件向子组件传递数据,子组件用defineProps接收父组件传递来的参数
在父组件中我们可以在使用子组件时,对其传递props数据
其中没有使用v-bind的数据为固定数据,如果使用v-bind即是动态的数据
子组件:
在子组件中接收数据
//defineProps是Vue3提供方法,不需要引入直接使用
let props = defineProps(['info', 'money'])
这种方式是简单写法,还有一种对象写法,用法更加的多,可以规定数据类型和默认值/
let props = defineProps({
info: {
type: String,
required: true, //是否规定必须得有
default: 'info默认值'
},
money: {
type: Number,
required: true, //是否规定必须得有
default: 9999 //默认数据,当父组件没有传递数据时,读取该数据
}
在子组件中使用也是很简单(这二种方式都可以)
{{props.info}}
{{props.money}}
{{info}}
{{money}}
注意点:props的数据为只读数据,不可以进行修改
父组件
在父组件中接收自定义事件
//事件回调---4
const handler3 = (val1, val2) => {
console.log(val1, val2)
}
子组件
主要是子组件利用defineEmits方法返回函数触发自定义事件
//利用defineEmits方法返回函数触发自定义事件
//defineEmits方法不需要引入直接使用
let $emit = defineEmits(['xxx'])
绑定事件传递参数
//按钮点击回调
const handler = () => {
//第一个参数:事件类型 第二个|三个|N参数即为注入数据
$emit('xxx', 'data1', 'data2')
}
点击按钮时会传递数据到父组件,父组件会接收到对应的参数
由于在Vue3中,其vue构造函数被移除,导致其没有了VM,无法做到$bus全局事件总线,要想实现全局事件总线可以使用mitt插件来实现
可以在项目中安装mitt
npm install --save mitt
在需要传递数据的组件中 $bus.emit方法是传递数据
//引入$bus对象
import mitt from 'mitt'
const $bus = mitt()
//点击按钮回调
const handler = () => {
$bus.emit('car', { car: '法拉利' })
}
在接收数据的组件中,$bus.on即是接收数据的方法
import mitt from 'mitt'
const $bus = mitt()
//组合式API函数
import { onMounted } from 'vue'
//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(() => {
//第一个参数:即为事件类型 第二个参数:即为事件回调
$bus.on('car', (car) => {
console.log(car)
})
})
大家对v-model的印象可能只是觉得它可以用来实现表单数据的双向绑定,但其实它还可以用来实现父子组件数据同步
如果我们不使用v-model,要想让父子组件的数据同步,许哟同时用到props和自定义事件才可以实现,像下面这样:
父组件:
//props:父亲给儿子数据
子组件:
钱数:{{ modelValue }}
可以看到我们需要同时用到了props和自定义事件可以实现父子组件的数据同步
使用v-model是实现:
只需要修改子组件的标签就行
v-model在组件身上使用
1:相当有给子组件传递props[modelValue] = 10000
2:相当于给子组件绑定自定义事件update:modelValue
父组件:
//父亲的数据
let pageNo = ref(1)
let pageSize = ref(3)
子组件接收:
同时绑定多个v-model
其主要实现原理还是利用了props和自定义事件的组合使用,v-model只是帮我们同步了数据和方法
父组件:将message属性传递给子组件
子组件:用useAttrs接收并展示数据
{{ $attrs.message }}
这样就实现了子组件接收父组件的数据
useAttrs的功能于props的功能很类似,都是父组件传递数据给子组件,如果使用了props和useAttrs同时接收数据,props的优先级比useAttrs高
ref:可以获取真实的DOM节点,可以获取到子组件实例VC,在父组件中拿到子组件的实例,那么就可以操作子组件的属性及方法了,默认情况下是不能拿到的,子组件需要对外暴露才行
父组件:
我是父组件:{{money}}
$parent:可以在子组件内部获取到父组件的实例
父组件只要放子组件即可:
在子组件中用$parent获取到父组件的实例对象,当然我们的父组件也需要对外暴露才能让子组件拿到实例对象
我是子组件{{money}}
父组件:用provide传输对应的数据,并提供一个key,后续的子组件在拿数据也是根据此key
Provide与Inject{{car}}
子组件:使用inject和对应的key获取到对应的数据
我是子组件1{{ car }}
孙组件:数据可以进行修改,而且所有的组件数据都是同步的
孙子组件
{{car}}
相比于 Vuex,Pinia 提供了更简洁直接的 API,并提供了组合式风格的 API,最重要的是,在使用 TypeScript 时它提供了更完善的类型推导。
这也是vue官方更加推荐的状态集中管理工具的原因
在使用pinia时需要安装其依赖
npm i pinia
pinia可以支持vue2和vue3,所以有二种写法,组合式和选择式
1.新建仓库文件store创建index.ts大仓库
//创建大仓库
import { createPinia } from 'pinia';
//createPinia方法可以用于创建大仓库
let store = createPinia();
//对外暴露,安装仓库
export default store;
2.在mian.js中引用
//引入仓库
import store from './store'
//使用
app.use(store)
state存放数据
actions实现方法(可以之间修改数据)
getters计算属性
//定义info小仓库
import { defineStore } from "pinia";
//第一个仓库:小仓库名字 第二个参数:小仓库配置对象
//defineStore方法执行会返回一个函数,函数作用就是让组件可以获取到仓库数据
let useInfoStore = defineStore("info", {
//存储数据:state
state: () => {
return {
count: 99,
arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
},
actions: {
//注意:函数没有context上下文对象
//没有commit、没有mutations去修改数据
updateNum(a: number, b: number) {
this.count += a;
}
},
getters: {
}
});
//对外暴露方法
export default useInfoStore;
在组件中使用pinia数据
{{ infoStore.count }}---{{infoStore.total}}
//定义组合式API仓库
import { defineStore } from "pinia";
import { ref, computed } from 'vue';
//创建小仓库
let useTodoStore = defineStore('todo', () => {
let arr = ref([1,2,3,4,5]);
const total = computed(() => {
return arr.value.reduce((prev, next) => {
return prev + next;
}, 0)
})
function updateTodo() {
arr.value.push(0)
}
//务必要返回一个对象:属性与方法可以提供给组件使用
return {
arr,
total,
updateTodo
}
});
export default useTodoStore;
在组件中使用:
{{ todoStore.arr }}