这里就以setup语法糖来介绍,因为毕竟setup语法糖还是更方便些嘛
1. 父传子
父传子的话,父组件没什么不同,还是跟之前一样的写法、一样的操作,我们主要关心子组件接受的方式,常用有两种方式如下:
先上个父组件的代码
<template>
<h1>我是父组件</h1>
<Comp :msg="msg" :info="info" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Comp from '@/components/Comp.vue'
const msg = ref('message')
const info = ref({
id: 0,
name: 'wft',
color: 'red'
})
</script>
然后子组件接受
方式一:
<template>
<div :style="{ backgroundColor: info.color }">我是子组件</div>
<p>msg: {{ msg }}</p>
<p>name: {{ info.name }}</p>
</template>
<script setup lang="ts">
import type { PropType } from 'vue'
type Info = {
id: number,
name: string,
color: string
}
const props = defineProps({
msg: {
type: String,
default: 'msg'
},
info: {
type: Object as PropType<Info>,
default: () => ({id: 1212, name: '哈哈', color: '#f5f5f5'})
}
})
</script>
方式二:
<template>
<div :style="{ backgroundColor: info.color }">我是子组件</div>
<p>msg: {{ msg }}</p>
<p>name: {{ info.name }}</p>
</template>
<script setup lang="ts">
type Info = {
id: number,
name: string,
color: string
}
const props = withDefaults(defineProps<{ msg: string, info: Info }>(), {
msg: '默认值',
info: () => ({ id: 0, name: 'defaultname', color: '#ccc' }) //默认值
})
</script>
如果不想要默认值的话,去掉withDefaults就可以,如下:
<script setup lang="ts">
type Info = {
id: number,
name: string,
color: string
}
const props = defineProps<{ msg: string, info: Info }>()
</script>
2. 子传父
子组件
<template>
<div>我是子组件</div>
<button @click="sonSend">我是子组件的按钮</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const msg = ref('我是子组件的msg')
type EmitType = {
(e: 'onSendMsg', params: string): void
}
const emit = defineEmits<EmitType>()
const sonSend = () => {
emit('onSendMsg', msg.value)
}
</script>
父组件
<template>
<h1>我是父组件</h1>
<Comp @onSendMsg="onSendMsg" />
</template>
<script setup lang="ts">
import Comp from '@/components/Comp.vue'
const onSendMsg = (val: string) => {
console.log(val, '子组件传过来的Msg');
}
</script>
3. 兄弟传值
这里采用第三方mitt库,其核心也是基于发布订阅的JS事件,这里就以两个兄弟为例
① 安装
npm i mitt
② main.ts中配置
代码
import { createApp, ComponentCustomProperties } from "vue";
import App from "./App.vue";
import store, { key } from "./store";
import router from './router'
import 'element-plus/dist/index.css'
import { createPinia } from 'pinia'
// mitt库
import mitt from 'mitt'
const Mit = mitt()
declare module "vue" {
export interface ComponentCustomProperties {
$bus: typeof Mit
}
}
const pinia = createPinia()
const app = createApp(App)
app.use(store, key).use(router).use(pinia)
// 挂载全局
app.config.globalProperties.$bus = Mit
app.mount("#app");
③ 使用
兄弟1
<template>
<div>我是兄弟1</div>
<button @click="onSendMsg">我是兄弟1的button</button>
</template>
<script setup lang="ts">
import { getCurrentInstance, ref } from 'vue'
const instance = getCurrentInstance()
const msg = ref('我是兄弟1')
const onSendMsg = () => {
instance?.proxy?.$bus.emit('onSendMsg', msg.value)
}
</script>
兄弟2
<template>
<div>我是兄弟2</div>
</template>
<script setup lang="ts">
import { getCurrentInstance, onMounted, onUnmounted } from 'vue'
const instance = getCurrentInstance()
onMounted(() => {
instance?.proxy?.$bus.on('onSendMsg', receiveMsg)
})
onUnmounted(() => {
instance?.proxy?.$bus.off('onSendMsg', receiveMsg)
})
function receiveMsg(val:unknown) {
console.log(val, '这是兄弟传过来的')
}
</script>
<style scoped>
</style>
兄弟传值的话当然也可以手写一个基于发布订阅的JS事件也是可以实现的,我前面也有写过,应该是JS方法手写系列,这里就不再赘述。
除了上面三种方式,组件通信还有其他的一些,比如provide、inject(这种就不管层级了,provide直接传递给所有的子孙后代了,通过injetc注入,但是这种方式用的不多);或者存储到vuex中或者pinia中都可以,vuex和pinia 前面也都有写过,这里也不再赘述,有兴趣的煲仔们可以去翻翻我之前的博文
这篇组件通信主要是介绍了vue3+ts的写法,之前有写过非ts的写法,有兴趣可以瞅瞅vite2 + Vue3 中 组件传值
其实加上ts也没什么,可能写法上有些不同,但核心就是增加了类型的约束,强类型支持