Vue3 的组件间通信:
- props
- $emit
- expose / ref
- $attrs
- v-model
- provide / inject
- Vuex
- mitt
Vue3 的一些变化(详见文档)
- 组合式API让Vue3 逻辑关注点聚合,让人不再有一种配置对象的感觉。
- teleport 传送组件到指定位置渲染,
- 片段,即支持多根节点—多根节点的时候需要,显式指定 attribute 的分布位置
自定义事件 - 单文件组件组合式 API 语法糖 (<script setup>)
Vue3 可以和Vue 2 写一样的结构,这里没有使用和Vue2一样的写法,而是使用单文件组件组合式 API 语法糖 (<script setup>)作为例子(react 版vue,还是vue版react呢,O(∩_∩)O哈哈~)。
props
父级组件向子组件传递信息,和Vue2 没什么差别,可以和Vue 2 实现的一样,也可以像下面尝试一些新的语法糖。
defineProps 和 defineEmits 都是只在 <script setup> 中才能使用的编译器宏。他们不需要导入且会随着 <script setup> 处理过程一同被编译掉。
<template>
<!-- parent.vue -->
<HelloWorld msg="Welcome to Your Vue.js App" :msg2="msg" />
</template>
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import { ref } from "vue";
const msg = ref("hello world");
</script>
<template>
<!-- child.vue -->
<div>{{prop.msg2}}</div>
</template>
<script setup>
// const props = defineProps(['msg2']);
const prop = defineProps({
msg2:String,
});
</script>
$emit
Vue3 中组件emit的时候都需要在定义的时候声明(不声明的话会输出一个警告),同时还可以在声明的时候对抛出的事件进行验证,这点变的和 prop 类似;
同时emits 选项中列出的事件不会从组件的根元素继承,也将从 $attrs property 中移除,孙子组件也就无法触发对应的监听器了。
<template>
<!-- parent.vue -->
{{msg}}
<HelloWorld msg="Welcome to Your Vue.js App" @update-data="msg++"/>
</template>
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import { ref } from "vue";
const msg = ref(1);
</script>
<template>
<!-- child.vue -->
<div @click="emit('update-data')">更新数据</div>
</template>
<script setup>
const emit = defineEmits(['update-data']);
</script>
expose / ref
expose 用于指定组件实例可以暴露那些属性和方法,没有添加这个属性的时候默认和Vue2 相同暴露全部属性和方法,添加这个属性之后就会只暴露指定的属性和方法了。
使用 <script setup> 的组件是默认关闭的,也即通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。所以需要使用 ‘defineExpose’来暴露需要使用的属性和方法。
<template>
<!-- parent.vue -->
{{msg}}
<HelloWorld ref="Otis" msg="Welcome to Your Vue.js App" @update-data="msg++" @click="handleClick"/>
</template>
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import { ref } from "vue";
const msg = ref(1);
const Otis = ref(null);
//Otis.value.q <---- 不能在这里访问,因为这个时候还没有子组件呢
const handleClick = () =>{
console.log(Otis.value.q);
}
</script>
<template>
<!-- child.vue -->
<div @click="emit('update-data')">更新数据 {{q}}</div>
</template>
<script setup>
import {ref} from 'vue';
const emit = defineEmits(['update-data']);
const q = ref(56);
defineExpose({
q,
})
</script>
$attrs
$attrs 现在包含了所有传递给组件的 attribute,包括 class 和 style。
在 Vue 3 的虚拟 DOM 中,事件监听器现在只是以 on 为前缀的 attribute,这样它就成为了 $attrs 对象的一部分,因此 $listeners 被移除了。
<template>
<!-- parent.vue -->
{{msg}}
<HelloWorld msg="Welcome to Your Vue.js App" @update-data="msg++" @up="msg++"/>
</template>
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import { ref } from "vue";
const msg = ref(1);
</script>
<template>
<!-- child.vue -->
<div @click="emit('update-data')">更新数据 {{q}}</div>
</template>
<script setup>
import {useAttrs} from 'vue';
const emit = defineEmits(['update-data']);
const attrs = useAttrs() // props和emit 没有接收的属性和监听器都会在这里
console.log(attrs);
</script>
v-model
支持多个数据进行双向绑定,相当于vue2 中的v-model融合了sync。默认的事件是update:modelValue,默认属性为modelValue。同时v-model 还支持自定义修饰符。
<template>
<!-- parent.vue -->
<HelloWorld msg="Welcome to Your Vue.js App" v-model:keys="msg" v-model="msg2"/>
</template>
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import { ref } from "vue";
const msg = ref(22);
const msg2 = ref(1);
</script>
<template>
<!-- child.vue -->
<div @click="emit('update:keys',a++),emit('update:modelValue',b++)">
更新数据 {{ props.keys }}--- {{ props.modelValue }}
</div>
</template>
<script setup>
import { ref } from "vue";
const a = ref(0);
const b = ref(33)
const emit = defineEmits(["update:keys","update:modelValue"]);
const props = defineProps(["keys", "modelValue"]);
</script>
provide / inject
和Vue2 相比变化不大,
<template>
<!-- parent.vue -->
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import { provide } from "vue";
provide("location", 'Noth Pole');
</script>
<template>
<!-- child.vue -->
<div>
更新数据 {{ out }}
</div>
</template>
<script setup>
import { inject } from "vue";
const out = inject('location',"defalult value");
</script>
mitt
Vue3 中没有了 EventBus 跨组件通信,但是现在有了一个替代的方案 mitt.js。
其它
Vue2 中的 $root,$children,$parent,Vuex,slot之类的通信依旧可用没什么变化就不写了。