你知道的“React18与Vue3组件通信”都在这
Vue3
1. 父传子
在 Vue 3 中,父组件通过 props 向子组件传递数据,子组件通过 defineProps 来声明接收的 props。
<template><!-- 使用‘v-bind:name’将父组件中的parentName变量的值动态绑定到子组件的 `name` 属性上--><Son :name="parentName" :age="parentAge" /></template><script setup>import { ref } from 'vue';import Son from './Son.vue';const parentName = ref('李四');const parentAge = ref(20);</script>
<!-- Son.vue --><template><div><h2>姓名:{{ name }}</h2><h2>年龄:{{ age }}</h2></div></template><script setup>// 子组件 defineProps 进行接收defineProps({name: {type: String,required: true}age: Number});</script>
「问:如何在父组件调用子组件时传递方法,并让父组件获取子组件内部的方法或属性?」
子组件需要明确地暴露这些方法和属性。这可以通过使用 defineExpose 来实现。随后,父组件可以通过 ref 或 template ref 来引用子组件实例,从而调用其暴露的方法或访问其属性。
❝具体步骤如下:
「子组件暴露方法和属性」:
在子组件中,使用 defineExpose来指定哪些方法或属性应该被父组件访问。「父组件引用子组件」:
父组件通过 ref或template ref获取对子组件实例的引用。一旦子组件挂载完成,父组件就可以通过这个引用来调用子组件的方法或读取其属性。 「父组件向子组件传递方法」: ❞
如果需要从父组件向子组件传递方法,可以通过 props实现。子组件可以接收这些方法并通过emit触发事件来回调父组件的方法。(后文兄弟通信,也使用到)
// 子组件<template><div><h1>Hello child!</h1></div></template><script setup>import { defineExpose } from 'vue';// 暴露出去defineExpose({foo() {console.log('这是来自子组件的foo方法');},childName:'这是子组件的属性'})</script>
//父组件<template><div><Child ref="cmp"/> <!-- 模版中标记,直接访问dom元素,或是子组件的实例 --><button @click="handleClick">按钮</button></div></template><script setup>import {ref} from 'vue'import Child from './Child.vue';const cmp = ref(null); // null表示未挂载function handleClick() {cmp.value.foo();cmp.value.childName = '修改了子组件的属性';console.log(cmp.value);}</script>
当子组件方法或是属性发生变化,父组件能够ref自动更新。相当于子组件挂载完毕后,使用ref响应式引用,父组件就可以在需要时调用其方法或访问其属性。注意ref拿值时,得.value。
2. 子传父
在 Vue 3 中,子组件通过 $emit 向父组件发送事件,父组件通过 v-on 监听该dom事件。使用defineEmit或是thia.$emit将子组件的方法或是属性传递给父组件使用。
<!-- Parent.vue --><template><!-- @ v-on 简写,使用 v-on 监听子组件的 sendMessage 事件 --><Son @sendMessage="receiveMessage" /><p>{{ message }}</p></template><script setup>import { ref } from 'vue';import Son from './Son.vue';const message = ref('');function receiveMessage(msg) {message.value = msg;}</script>
<!-- Son.vue --><template><button @click="sendMessage">点击传递消息</button></template><script setup>const emit = defineEmits();function sendMessage() {// 触发 'sendMessage' 事件,并传递消息作为参数emit('sendMessage', '我是Son组件');}</script>
上面defineEmits() 不用声明事件,可以传递任何事件,但同时也失去了对事件的类型检查和编译时验证。多项目中,未知事件传递带来风险。下面采用「对象和数组」两种方式简单说明,defineEmits()可以进行的检查和编译
数组形式示例
const emit = defineEmits(['sendMessage', 'updateData', 'closeModal']);对象形式示例(TypeScript)
<script setup lang="ts">interface Emit {(e: 'sendMessage', message: string): void;(e: 'updateData', id: number, data: object): void;(e: 'closeModal'): void;}// 明确声明可以发射的事件,并提供类型检查const emit = defineEmits<Emit>();</script>
02| 跨层组件通信:使用 context VS provide 实现
跨层通信由父App组件传给A里面的B组件
React
在 React 中,跨层组件通信可以通过 「Context API」 来实现。Context 允许你在组件树中传递数据,而不必手动通过每一层的 props。这种机制对于需要跨越多层嵌套的组件来说非常有用。
1.createContext 创建 context 对象,上下文ctx 对象将包含你希望在组件树中共享的数据。 2.Provider 提供器,包裹需要跨层通信的组件。将数据通过 value属性传递给Provider,任何位于Provider内部的组件都可以访问到这些数据。3. useContext钩子来访问上下文中的数据。useContext接受一个上下文对象(就是通过createContext创建的那个对象)作为参数,并返回该上下文的当前值。
import React, { createContext, useContext } from 'react';// 1. 创建 context 对象const MsgContext = createContext();function ContextApp() {const msg = 'this is app';return (<>{/* 2. 使用 Provider 包裹组件并提供数据 */}<MsgContext.Provider value={msg}><div><h2>我是 App 组件</h2><A /></div></MsgContext.Provider></>);}function A() {const sonMsg = '我是 son1 子组件';return (<>{sonMsg}<B /></>);}function B() {// 3. 使用 useContext 钩子接收数据const msg = useContext(MsgContext);return (<><div>this is B,{msg}</div></>);}export default ContextApp;
Vue 3
在 Vue 3 中,跨层组件通信可以通过 「provide 和 inject」 API 来实现。provide 用于提供数据,而 inject 用于在后代组件中获取这些数据。这是 Vue 中实现跨层组件通信的标准方法(其实也可以使用pinia🍍全局管理共享数据store)
<!-- Parent.vue --><template><div><h2>我是 App 组件</h2><A /></div></template><script setup>import { provide } from 'vue';import A from './A.vue';const msg = 'this is app';// 1. 使用 provide 提供数据provide('msg', msg);</script>
<!-- A.vue --><template><div>我是 son1 子组件<B /></div></template><script setup>import B from './B.vue';</script>
<!-- B.vue --><template><div>this is B,{{ msg }}</div></template><script setup>import { inject } from 'vue';// 2. 使用 inject 获取数据const msg = inject('msg');</script>
对比:React 与 Vue 的跨层通信
| 特性 | React | Vue 3 |
|---|---|---|
| 「跨层通信方式」 | context 和 useContext | provide 和 inject |
| 「数据传递方向」 | Provider 向下传递数据, Consumer 获取数据 | provide 在父组件提供数据, inject 在后代组件获取数据 |
| 「适用场景」 | 适用于中等规模应用,尤其是需要共享全局状态时 | 适用于需要跨越多个层级传递数据的场景 |
03| 兄弟组件通信
React
兄弟组件的通信通过将「共享的状态提升」到父组件来实现。父组件将该状态和修改状态的函数通过 props 传递给子组件。
import React, { useState } from 'react';function Parent() {const [msg, setMsg] = useState('');const getMsg = (msg) => {setMsg(msg);};return (<div><h2>父组件状态:{msg}</h2><Son1 getMsg={getMsg} /><Son2 msg={msg} /></div>);}function Son1({ getMsg }) {const sonMsg = '我是Son1子组件';return <button onClick={() => getMsg(sonMsg)}>传递消息</button>;}function Son2({ msg }) {return <h2>兄弟组件接收到的消息:{msg}</h2>;}
Vue
在 Vue 3 中,兄弟组件的通信也通过将共享的状态提升到父组件来实现。父组件通过 props 向子组件传递数据,子组件通过 emit 向父组件传递事件。
<!-- Parent.vue --><template><Son1 @sendMessage="receiveMessage" /><Son2 :msg="message" /></template><script setup>import { ref } from 'vue';import Son1 from './Son1.vue';import Son2 from './Son2.vue';const message = ref('');function receiveMessage(msg) {message.value = msg;}</script>
<!-- Son1.vue --><template><button @click="sendMessage">传递消息</button></template><script setup>const emit = defineEmits();function sendMessage() {emit('sendMessage', '我是Son1子组件');}</script>
<!-- Son2.vue --><template><h2>兄弟组件接收到的消息:{{ msg }}</h2></template><script setup>defineProps({msg: String,required:true // 必须传});</script>
总结
React18和Vue3 机制都是为了简化,Vue3哲学注重业务开发,所以通过很多对应的语法。比如,props接收参数,'v-bind'等。React18 适合大型项目,在管理项目和JavaScript运用上面都有深入。就以跨层组件通信来说,通过 「Context」(React)或 「provide/inject」(Vue 3) API,开发者可以更加方便地进行跨层组件通信,尤其是当数据需要在深层嵌套的组件间共享时,能极大简化组件的结构和数据流动。