Vue组件通讯

总览

  1. props/emit 父子组件通信
  1. provide/inject 祖孙组件通信
  1. useAttrs 传递父作用域里除 class 和 style 除外的非 props 属性集合
  1. vuex/pinia 通用
  1. v-model
  1. ref/expose
 
前面4中比较常见,这里就不啰嗦了,主要记录一下后两种的用法。

v-model

v-model不仅可以用作DOM节点的双向绑定,也可以用于组件之间。
假设A组件是父组件,B组件是子组件
// A.vue <script setup> import { ref } from "vue"; import B from "./B.vue"; const count = ref(1); </script> <template> <span>{{ count }}</span> <B v-model="count" /> </template>
当v-model用于组件之间时,可以看做是
<B :modelValue="count" @update:model-value="count = $event" />
子组件B.vue
// B.vue <script setup> const emit = defineEmits(["update:modelValue"]); const props = defineProps(["modelValue"]); console.log(props.modelValue); // 获取prop // 更新modelValue function onClick() { emit("update:modelValue", props.modelValue + 1); } </script>
v-model相当于v-model:modelValue ,也可以传递其他参数,例如v-model:count ,如果这样子组件的modelValue也要改为count。
// A.vue <B v-model:count="count" /> // B.vue <script setup> const emit = defineEmits(["update:count"]); const props = defineProps(["count"]); console.log(props.count); function onClick() { emit("update:count", props.count + 1); } </script>

ref/expose

子组件通过defineExpose主动暴露自身的数据,父组件通过ref可以获取这些变量和函数。
// A.vue <B ref="bRef" /> <script setup> import { ref } from "vue"; import B from "./B.vue"; const bRef = ref(null); function onClick() { console.log(bRef.value.a); console.log(bRef.value.b); console.log(bRef.value.c); console.log(bRef.value.validate()); } </script> // B.vue <script setup> import { ref } from "vue"; const value = ref("123"); // 假设这是要传递的数据 function validate() { return value; } const a = 1; const b = ref(2); const c = reactive({ c: 3 }); defineExpose({ a, b, c, validate, }); </script>
这里有一点需要注意:
  • 如果expose的是ref,并且是一个原始值,那么父组件获取的是一个非响应式数据
  • 如果expose的是reactive或者引用类型的ref,那么父组件获取的是一个响应式数据
  • 如果expose的是一个函数,那么函数中可以访问原组件的响应式数据
不要将子组件的响应式数据传递到父组件中,因为这不符合单向数据流原则,会导致项目变得难以维护,在传递响应式数据前最好先消除响应。
const count = ref(1); const obj = reactive({ a: 1 }); const o = ref(reactive({ a: 10 })); function validate() { return count.value; return JSON.parse(JSON.stringify(toRaw(obj))); return JSON.parse(JSON.stringify(toRaw(o.value))); }
toRaw函数返回响应式数据的原始对象,但是我们最好不要直接操作这个对象,最好的方法还是返回一个深拷贝版本,使用JSON.parse(JSON.stringify(obj))可以实现一个简易版本的深拷贝。
defineExpose可以用于表单组件,可以暴露一个校验表单数据的函数,并在函数中返回数据。
const obj = reactive({ a: 1 }); // 假设这是一个表单数据 function validate() { // 校验规则 const data = JSON.parse(JSON.stringify(toRaw(obj))); if (!validateForm(data)) { return Promise.reject("错误"); } return Promise.resolve(data); }