借助父组件传参
交替使用emit和prop,以父组件为桥梁进行兄弟间的传参。
App.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <template> <div> <!-- 这两个子组件是平级的 --> <A @on-click="getFlag"></A> <B :flag="flag"></B> </div> </template>
<script setup lang="ts"> import { ref } from "vue" import A from "@/components/A.vue" import B from "@/components/B.vue"
let flag = ref(false) // 父组件将接收到的A组件的传值传给B组件 const getFlag = (param: boolean)=> { flag.value = param } </script>
|
A.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <div class="a"> <h1>A组件</h1> <button @click="emitB">派发一个事件</button> </div> </template>
<script setup lang="ts"> const emit = defineEmits(["on-click"]) let flag = false // 通过自定义事件给父组件App.vue传递数据 const emitB = ()=> { flag = !flag emit("on-click", flag) } </script>
<style scoped lang="scss"> .a { width: 200px; height: 200px; background-color: cyan; } </style>
|
B.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <div class="b"> <h1>B组件</h1> {{ flag }} </div> </template>
<script setup lang="ts"> defineProps<{ flag: boolean }>() </script>
<style scoped lang="scss"> .b { width: 200px; height: 200px; background-color: orange; } </style>
|
但是这样太麻烦了,每次都要在父组件这里处理逻辑。
借助Event Bus传参
简单实现一个发布者-订阅者模式。
bus.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 25 26 27 28 29 30 31 32 33 34 35 36
| type BusClass = { emit:(name:string)=>void on:(name:string,callback:Function)=>void }
type ParamsKey = string | number | symbol
type List = { [key:ParamsKey]:Array<Function> }
class Bus implements BusClass { list:List
constructor() { this.list = {} }
emit(name:string,...args:Array<any>) { const eventName: Array<Function> = this.list[name] eventName.forEach(fn=> { fn.apply(this,args) }) } on(name:string,callback:Function) { const fn:Array<Function> = this.list[name] || [] fn.push(callback) this.list[name] = fn } }
export default new Bus()
|
App.vue:
1 2 3 4 5 6 7 8 9 10 11
| <template> <div> <A></A> <B></B> </div> </template>
<script setup lang="ts"> import A from "@/components/A.vue" import B from "@/components/B.vue" </script>
|
A.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <div class="a"> <h1>A组件</h1> <button @click="emitB">派发一个事件</button> </div> </template>
<script setup lang="ts"> import Bus from "../bus" let flag = false
const emitB = ()=> { flag = !flag Bus.emit("on-click",flag) } </script>
<style scoped lang="scss"> .a { width: 200px; height: 200px; background-color: cyan; } </style>
|
B.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <div class="b"> <h1>B组件</h1> {{ flag }} </div> </template>
<script setup lang="ts"> import { ref } from "vue" import Bus from "../bus"
let flag = ref(false) Bus.on("on-click", (f:boolean)=>{ flag.value = f }) </script>
<style scoped lang="scss"> .b { width: 200px; height: 200px; background-color: orange; } </style>
|
Mitt
在Vue2中常用全局事件总线,但Vue3中$on
、$off
和$once
实例方法都已被删除,组件实例不再实现事件触发接口,因此就不能像Vue2一样使用全局事件总线了。
作为替代,可以使用Mitt库(就是发布订阅模式)
在main.ts中将Mitt挂载到全局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import mitt from "mitt"
const Mit = mitt()
declare module "vue" { export interface ComponentCustomProperties { $Bus: typeof Mit } }
app.config.globalProperties.$Bus = Mit
|
App.vue:
1 2 3 4 5 6 7 8 9 10 11
| <template> <div> <A></A> <B></B> </div> </template>
<script setup lang="ts"> import A from "@/components/A.vue" import B from "@/components/B.vue" </script>
|
A.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <template> <div class="a"> <h1>A组件</h1> <button @click="emitB">派发一个事件</button> </div> </template>
<script setup lang="ts"> import { getCurrentInstance } from 'vue'
// 获取当前组件的实例 const instance = getCurrentInstance()
let flag = false
const emitB = ()=> { flag = !flag instance?.proxy?.$Bus.emit("on-click",flag) instance?.proxy?.$Bus.emit("on-str","strstr") } </script>
<style scoped lang="scss"> .a { width: 200px; height: 200px; background-color: cyan; } </style>
|
B.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <template> <div class="b"> <h1>B组件</h1> <button @click="cancelFlag">取消监听flag</button> <button @click="cancelAll">取消所有事件</button> {{ flag }} </div> </template>
<script setup lang="ts"> import { ref, getCurrentInstance } from "vue"
const instance = getCurrentInstance()
const cb = (f: unknown)=>{ flag.value = f as boolean }
let flag = ref(false) // 监听on-click flag变化的事件 instance?.proxy?.$Bus.on("on-click", cb)
// *表示监听所有的事件触发 instance?.proxy?.$Bus.on("*", (type, arg)=>{ // type:事件名称 其他参数:事件传参 console.log(type,arg) })
// 取消on-click flag的事件 const cancelFlag = ()=> { // 参数1:取消的事件名 参数2:取消的回调函数 instance?.proxy?.$Bus.off("on-click", cb) }
// 取消所有事件 const cancelAll = ()=> { instance?.proxy?.$Bus.all.clear() } </script>
<style scoped lang="scss"> .b { width: 200px; height: 200px; background-color: orange; } </style>
|
也可以通过引入mitt使用。