vue3组件间通信方式

1. props(父给子)

props:vue3中,子组件获取数据,直接defineProps([‘xxx’]),即可在模板中使用,但是props是只读的。

2. 自定义事件(子给父)

vue框架事件分为两种:原生DOM事件、自定义事件。

原生DOM事件会带回调函数,里面必定包含基础的event事件对象。

在vue3中,原生DOM事件不管放在标签or组件标签上,都是原生DOM事件。

自定义事件完成父子组件传递数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//子组件Event
<template>
<div>
<button @click='handler')">
点击出发自定义事件xxx
</button>
</div>
</template>

<script setup>
let a = defineEmits(['xxx'])
const handler = () => {
//第一个参数为事件类型xxx,后面为注入数据
a("xxx","aabb","sdd"...)
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
//父组件
<template>
<Event @xxx="handler">
</Event>
</template>

<script setup>
import Evenet from './Event.vue';
const handler = (event) => {
//展示数据
}
</script>

####3. 全局事件总线(兄弟之间)

使用mitt插件。

安装完成后,新建bus文件夹,里面新建index.ts。

1
2
3
import mitt from 'mitt';
const $bus = mitt();
export default $bus

接着即可完成兄弟组件数据通信。

1
2
3
4
5
6
7
8
9
10
11
12
13
//接收数据者
<script setup>
import $bus from '../../bus';
//组合API函数
import {onMounted} from 'vue';
//组件挂载完毕,当前组件绑定事件,接收兄弟组件传来的数据
onMounted(() => {
//第一个参数为事件类型,第二个参数为事件回调
$bus.on('aa', (e)=>{
console.log(e)
})
})
</script>
1
2
3
4
5
6
7
8
//发送数据者
<script setup>
import $bus from '../../bus';
//事件回调
const handler = () => {
$bus.emit('aa',{dd: "bb"})
}
</script>

4. v-model

v-model:基础用法为收集表单数据,完成数据双向绑定。

但是也可以实现组件之间通信!

v-model在组件上使用:相当于给子组件传递props[modelValue],绑定自定义事件update:modelValue

1
2
3
4
5
6
7
8
9
10
11
12
//父组件
<template>
<Event v-model:a="a" v-model:b="b"></Event>
</template>

<script setup>
import Evenet from './Event.vue';
import {ref} from 'vue';
//父数据
let a = ref("xxxx");
let b = ref("xxxxxx");
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//子组件Event
<template>
<div>
<button @click="handler">a{{a}}</button>
<button>b{{b}}</button>
</div>
</template>

<script setup>
let props = defineProps(['a', 'b']);
let $emit = defineEmits(['update:a', 'update:b'])
const handler = () => {
//第一个参数为事件类型xxx,后面为注入数据
$emit('update:a', props.a + 3);
}
</script>

5. useAttrs

此方法可以获取组件标签身上的属性与事件

1
2
3
4
//父组件
<template>
<HindButton type="primary" size="small"></HindButton>
</template>
1
2
3
4
5
6
7
8
9
10
11
//子组件HindButton
<template>
<div>
<el-button :="$attrs"></el-button>
</div>
</template>

<script setup>
import {useAttrs} from 'vue';
let $attrs = useAttrs();
</script>

注意:props与useAttrs方法都可以获取父组件的属性和属性值,但是props优先级更高。

6. ref与$parent

ref:可以获取真实的DOM节点,可以获取到子组件的实例VC。

$parent:可以获取到父组件的实例。

7. provide与inject

实现隔辈组件通信。

1
2
3
4
5
6
7
8
//祖先组件
<script setup>
import Evenet from './Event.vue';
import {ref, provide} from 'vue';
let car = ref("xasda");
//第一个参数为key,第二个参数为数据
provide("TOKEN", car)
</script>
1
2
3
4
5
//后辈组件Evenet
<script setup>
import {ref, inject} from 'vue';
let car = inject("TOKEN");
</script>

注意:可以修改数据,不是只读,和props不一样!

8. pinia

  • vuex:集中式管理状态容器,可以实现任意组件间通信!

    核心概念:state、mutations、actions、getters、modules。

  • pinia:集中式管理状态容器,可以实现任意组件间通信!

    核心概念:state、actions、getters。

  1. 选择式写法

    在store文件夹下新建文件index.ts,创建大仓库。

    1
    2
    3
    import {createPinia} from 'pinia';
    let store = createPinia();
    export default createPinia

    在main.js引入仓库。

    1
    2
    import store from './store';
    app.use(store);

    接着在store文件夹下创建modules文件夹,存放小仓库,新建info.ts。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import {defineStore} from 'pinia';

    //defineStore方法返回一个函数,让组件可以获取仓库数据
    //参数:小仓库名字+小仓库配置对象
    let useInfoStore = defineStore("info", {
    //存储数据:state
    state: () => {
    return {
    a: "asdasa"
    }
    },
    actions: {
    updateA(param){
    //直接对this.a进行修改
    }
    },
    //计算属性
    getters: {
    total(){
    //返回计算结果res
    }
    }
    })
    export default useInfoStore;

    此时,组件即可选取仓库,获取对应数据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <template>
    <div>
    <button @click="updateState">修改数据</button>
    </div>
    </template>

    <script setup>
    import {useInfoStore} from '../../store/modules/info.ts';
    let info = useInfoStore();

    const updateState = () => {
    //可以直接对info.a进行修改
    //也可以调用仓库里面actions自定义的方法
    }
    </script>
  2. 组合式写法

    在store/modules文件夹下,新建todo.ts。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import {defineStore} from 'pinia';
    import {ref} from 'vue';

    //参数:小仓库名字+小仓库配置对象
    let useTodoStore = defineStore("todo", () => {
    let todos = ref([{a: "asdasda"}]);

    //返回一个对象:属性和方法。以给组件使用
    return {
    todos,
    update(){
    ...
    }
    }
    });
    export default useTodoStore;

    接着组件开始调用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <template>
    <div>
    <button @click="updateTodo">修改数据</button>
    </div>
    </template>

    <script setup>
    import {useTodoStore} from '../../store/modules/todo.ts';
    let todo = useTodoStore();

    const updateTodo = () => {
    todo.update();
    }
    </script>

9. 插槽

  • 默认插槽

  • 具名插槽

    1
    2
    3
    //子组件Test
    <slot name="a"></slot>
    <slot name="b"></slot>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    父组件
    <Test>
    <template v-slot:a>
    ...
    </template>

    <template v-slot:b>
    ...
    </template>
    </Test>
  • 作用域插槽(很常用)

    可以传递数据的插槽,子组件可将数据回传给父组件,父组件决定如何将数据在子组件内展示。