Vue3中组件通讯的方式
1 🤖GPT🤖: (答案有点问题混淆了vue2的内容)
父组件向子组件传递数据
- props 子组件通过 props 属性从父组件接收数据。
- emit事件子组件通过emit 事件 子组件通过 emit事件子组件通过emit 发射事件向父组件发送消息。
- provide / inject 父组件通过 provide 提供数据,子组件通过 inject 注入数据。
- ref 父组件通过 ref 获取子组件实例,然后调用子组件的方法。
- Slots 在父组件中使用Slots向子组件传递内容,子组件可以通过$slots来获取这些内容,并将它们渲染到自己的视图中。
子组件向父组件传递数据
emit事件使用emit 事件 使用 emit事件使用emit 发射事件,父组件通过 v-on 监听该事件。
provide/inject 子组件通过 inject 注入父组件提供的数据,然后触发 Action 修改该数据,父组件的数据也就变了。
Ref 父组件通过 ref 获取子组件的实例,子组件调用 ref 内容,父组件监听其变化。
Vuex 使用 Vuex 管理共享状态,子组件通过 mutation 或 action 修改状态,父组件监听状态变化。
Event Bus 利用 Vue 的实例作为 EventBus ,子组件通过 Bus.$emit 触发事件,父组件监听该事件的变化。
兄弟组件之间的数据传递
EventBus 可以使用EventBus来创建一个全局的事件总线,从而在兄弟组件之间进行数据传递。通过在兄弟组件中使用emit和emit和emit和on来触发和监听事件,从而实现数据的传递。
Provide 和 Inject Vue 3中的Provide和Inject API也可以用于兄弟组件之间的数据传递,可以通过在父组件中使用Provide来提供数据,然后在兄弟组件中使用Inject来获取这些数据,从而实现兄弟组件之间的数据传递。
Vuex Vuex是Vue 3中的状态管理库,可以用于管理全局的数据状态,从而实现兄弟组件之间的数据传递。
父组件向子组件传递数据:(父传子)
1. props:
逐级透传
父组件
<template><ChildComponent :msg="message" />
</template>
<script setup> const message = 'hello'
</script>
子组件:
<template><p>{{ msg }}</p>
</template><script setup>defineProps(['msg'])
</script>
2. provide/inject
跨级共享
provide(提供): 父组件中注册,传递给后代组件的数据对象
inject( 注入):后代组件中接收父组件提供的数据对象
父组件 Root.vue
<template><div><Footer/></div>
</template>
<script setup>import { ref,provide } from 'vue'import Footer from './Footer.vue' //导入子组件Footer//父组件中注册给共享后代组件的信息provide(/* 注入名 */ 'mgs', /* 值 */ '父组件信息!')
</script>
子组件 Footer.vue
<template><DeepChild/>
</template>
<script setup>
import DeepChild from './DeepChild.vue' //导入子组件Footer
//在子组件Footer中可以不需要接收父组件Root共享的数据,在后代组件中,直接接收,实现跨级传递
</script>
子孙组件DeepChild.vue
不需要父组件Footer传递,就能跨级获取祖先组件Root传递的数据
<template><div>{{ msg }}</div>
</template>
<script setup>import { inject } from 'vue'const msg = inject('msg') //跨级接收祖先组件的共享的信息console.log(msg) //父组件信息
</script>
父组件向子组件传递信息,如果是多层组件嵌套(父>子>孙>孙孙…) ,props逐级透传十分麻烦,props可用但不优雅,更推荐 provide/inject依赖注入的方式;
在provide/inject依赖注入中父组件向后代组件通讯,如果说props是传递,逐级透传的,那么依赖注入更准确来说是在父组件中与后代组件共享数据,可实现跨级共享;
3. 透传 Attributes(非props和非emit)
“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 class、style 和 id
作用: 在父组件标签上声明的参数/事件监听,会透传到子组件中
3.1 Attributes透传参数
父组件
<!-- 透传参数class到子组件中 -->
<MyButton class="large" />
子组件 MyButton.vue
<button>click me</button>
最终渲染的html标签
<button class="btn large">click me</button>
3.2 Attributes透传事件监听(有点像冒泡事件)
父组件
<!-- 透传参数class到子组件中 --><MyButton @click="onClick1" /><script setup>import MyButton from './MyButton.vue'const onClick = ()=>{cosole.log("透传事件监听,从父组件触发")}</script>
子组件 MyButton.vue
<button @click ="onClick2">click me</button>
当点击子组件的按钮时:
父组件的onClick1 和子组件的onClick2 都触发
3.3 useAttrs 像defineProps获取透传Attributes
父组件
<!-- 透传参数ms到子组件中 -->
<Child msg="父组件中传递数据" /><script setup>import Child from './child.vue'
</script>
子组件child.vue
<script setup>
import { useAttrs } from 'vue'
//useAttrs像defineProps获取透传Attributes
const attrs = useAttrs()
cosole.log(attrs.msg) //父组件中传递数据
</script>
4. slot 插槽
父组件向子组件指定位置插入html内容渲染
4.1 默认插槽(传递html/组件)
父组件
<template><Child><div>插入的html,将会在子组件中指定slot的位置渲染出来</div></Child>
</template>
子组件
<template><slot><slot/>
</template>
4.2 具名插槽(父->子)
当需要渲染不同的内容时,默认插槽显然不够用,需要按插槽的name名进行区别渲染
父组件
#XX == v-slot:XX 这2个写法都是插槽name名在父组件的写法
<子><template #XX1> 插槽1 </template><template v-slot:XX2>插槽2</template>
</子>
子组件
<template><slot name="XX1"><slot/><slot name="XX2"><slot/>
</template>
4.3 作用域插槽 (子>父)
作用插槽分为: 默认作用域插槽和 具名作用域插槽
elementUI中table组件插入按钮就是使用了默认作用域插槽
4.3.1. 默认作用域插槽: v-slot:defalut = #defalut = v-slot
父组件
<子><template #default="slotProps">{{slotProps.XX}}</template>
</子>
子组件
<slot :XX="子组件数据"></slot>
4.3.2. 具名作用域插槽
父组件
<子><template #slotName="slotProps">{{slotProps.XX}}</template>
</子>
子组件
<slot name="slotName" :XX="子组件数据"></slot>
Element-PlusUI组件框架中table组件
table组件中就用到了作用域插槽
<el-table :data="tableData" style="width: 100%" max-height="250"><el-table-column fixed prop="date" label="Date" width="150" /><el-table-column fixed="right" label="Operations" width="120"><template #default="scope">{{scope.row.date}}</template></el-table-column>
</el-table>
子组件向父组件传递数据:(子传父)
1.组件事件emit
父组件中v-on(简写@)监听
子组件中$emit触发
父组件
父组件中v-on(简写@)监听
<template><child @some-event="callback" />
</template>
<script setup>
import child from './child.vue'
const callback = (target) => {console.log('父组件-callback ')console.log(target) //子组件传递的数据
}
</script>
子组件
子组件中$emit触发
<templete><!-->在templete中使用$emit触发,不需要defineEmits声明</--> <button @click="$emit('someEvent', '子组件传递的数据')">click me</button> <!-->触发方法中的emit,需要defineEmits声明</--> <button @click="buttonClick()">click me</button>
</templete><script setup>
//setup语法糖中显示声明emit
const emit = defineEmits(['someEvent'])
function buttonClick() {//触发emit emit('someEvent', '子组件传递的数据')
}
</script>
2. defineExpose/ ref
子组件中通过defineExpose向外暴露数据或方法
父组件中通过ref获取子组件暴露的数据或调用子组件的方法
父组件
在父组件中需要声明子组件的ref, 如:const childRef = ref()
<template><child ref="childRef" />
</template>
<script setup>
import { ref, onMounted } from 'vue'
// 引入子组件
import child from './child.vue'const childRef = ref()
onMounted(() => {console.log(childRef.value.data1) // 子组件数据childRef.value.fn() // 子组件中的方法
})
</script>
子组件
子组件中通过defineExpose向外暴露数据或方法
<script setup>
import { ref } from 'vue'
const data1 = ref('子组件数据')
const fn = () => {console.log('子组件中的方法')
}
//通过defineExpose向外暴露数据或方法
defineExpose({data1,fn
})
</script>
跨组件通讯-全局状态共享(状态管理库): Vuex /Pinia
在Vue3已经逐渐用Pinia这个菠萝替代Vuex了
Pinia,官方文档描述:符合直觉的 Vue.js 状态管理库 hook的写法!
其实Pinia的官方文档就写得很清楚了:🛬🛬🛬🛬🛬🛬🛬🛬🛬🛬🛬🛬为什么你应该使用 Pinia?