vue3组件传值共8种方式
1. props传值
props适用于父传子,有两种写法,混合写法和纯vue3写法(语法糖)
1. 混合写法(不推荐!!!)
// 父组件
<script setup lang="ts">
import child from '../components/Child.vue'
</script>
<template>
<child msg="父组件的消息"/>
</template>
// 子组件
<script>
export default {
props: ["msg"], // 这行要写,不然下面接收不到
setup(props) {
console.log(props)
},
}
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
</div>
</template>
2. 纯 Vue3 写法(语法糖)
父组件变,子组件如下:
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
</div>
</template>
当js逻辑中需要使用传过来的props时,也可以定义一个变量承接一下:
<script setup lang="ts">
const props = defineProps<{
msg: string
}>()
console.log(props) // Proxy {msg: '父组件的消息'}
</script>
注意:如果父组件是混合写法,子组件纯 Vue3 写法的话,是接收不到父组件里 data 的属性,只能接收到父组件里 setup 函数里传的属性
如果父组件是纯 Vue3 写法,子组件混合写法,可以通过 props 接收到 data 和 setup 函数里的属性,但是子组件要是在 setup 里接收,同样只能接收到父组件中 setup 函数里的属性,接收不到 data 里的属性。所以官方也不推荐混合写法。
2. attrs传值(父传子)
attrs包含了父作用域里除 class 和 style 除外的非props 属性集合
// 父组件
<script setup lang="ts">
import { reactive } from 'vue'
import child from '../components/Child.vue'
const info = reactive({foo: 111})
console.log(info)
</script>
<template>
<child msg="父组件的消息" :info="info" />
</template>
// 子组件
<script setup lang="ts">
import { useAttrs } from "vue"
const attrs = useAttrs()
console.log(attrs)
</script>
<template>
<div class="greetings">
<!-- <h1 class="green">{{ msg }} -- {{ info.foo }}</h1> -->
<h1 class="green">{{ attrs.msg }} -- {{ attrs.info.foo }}</h1>
</div>
</template>
3. expose/ref (子传父)
父组件获取子组件的属性或者调用子组件方法
// 子组件
<script setup lang="ts">
defineExpose({
childInfo: '子组件的信息',
greet() {
console.log('来自子组件的问候')
}
})
</script>
// 父组件
<script setup lang="ts">
import { reactive, ref } from 'vue'
import child from '../components/Child.vue'
const info = reactive({foo: 111})
// console.log(info)
const comp = ref()
const handlerClick = () => {
console.log(comp.value.childInfo) // 获取子组件对外暴露的属性
comp.value.greet() // 调用子组件对外暴露的方法
}
</script>
<template>
<child ref="comp" msg="父组件的消息" :info="info" />
<button @click="handlerClick">按钮</button>
</template>
4. v-model
支持多个数据双向绑定
// Parent.vue
<script setup>
import child from '../components/Child.vue'
import { ref, reactive } from "vue"
const key = ref("1111")
const value = ref("2222")
</script>
<template>
<child ref="comp" msg="父组件的消息" :info="info" v-model:key="key" v-model:value="value"/>
</template>
// Child.vue
<template>
<h1 class="green" @click="handlerClick">{{ attrs.msg }} -- {{ attrs.info.foo }}</h1>
</template>
<script setup>
const emit = defineEmits(['key', 'value'])
const handlerClick = () => {
emit('update:key', '新的key')
emit('update:value', '新的value')
}
</script>
5. $emit
// 子组件
<script setup lang="ts">
const emit = defineEmits(['childClick'])
const clickMe = () => {
emit('childClick', '子组件的信息')
}
</script>
<template>
<div class="greetings">
<button @click="clickMe">点我</button>
</div>
</template>
// 父组件
<script setup lang="ts">
const onChildClick = (msg: string) => {
console.log(msg)
}
</script>
<template>
<child ref="comp" msg="父组件的消息" :info="info" v-model:key="key" v-model:value="value" @childClick="onChildClick"/>
</template>
在子组件中也可以直接调用$emit方法:
<button @click="$emit('childClick', '子组件的信息')">点我</button>
注意:在 中使用的 $emit 方法不能在组件的
6. provide / inject(跨代传值)
provide: 提供一个值,可以被后代组件注入。
inject: 注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。
// Parent.vue
<script setup>
import { provide } from "vue"
provide('parent', 'parent的值')
</script>
// Child.vue
<script setup>
import { inject } from "vue"
const value = inject('parent')
console.log(value) // parent的值
</script>
7. mitt
Vue3 中没有了 EventBus 跨组件通信,但是现在有了一个替代的方案 mitt.js,原理还是 EventBus
使用步骤:
// 安装mitt
cnpm i mitt -S
封装:
mitt.js
import mitt from 'mitt'
const mitt = mitt()
export default mitt
使用:
// 组件 A
<script setup>
import mitt from './mitt'
const handleClick = () => {
mitt.emit('handleChange')
}
</script>
// 组件 B
<script setup>
import mitt from './mitt'
import { onUnmounted } from 'vue'
const someMethed = () => { ... }
mitt.on('handleChange',someMethed)
onUnmounted(()=>{
mitt.off('handleChange',someMethed)
})
</script>
8. vuex / pinia(跨代传值)
pinia之前有篇详细介绍:pinia指南
vuex:
// store/index.js
import { createStore } from "vuex"
export default createStore({
state:{ count: 1 },
getters:{
getCount: state => state.count
},
mutations:{
add(state){
state.count++
}
}
})
// main.js
import { createApp } from "vue"
import App from "./App.vue"
import store from "./store"
createApp(App).use(store).mount("#app")
// Page.vue
<template>
<div>{{ $store.state.count }}</div>
<button @click="$store.commit('add')">按钮</button>
</template>
<script setup>
import { useStore, computed } from "vuex"
const store = useStore()
console.log(store.state.count) // 1
const count = computed(()=>store.state.count) // 响应式,会随着vuex数据改变而改变
console.log(count) // 1
</script>