Vue 的父子传输数据全解析
常见的父子传输数据的操作,基于 vue3.0,不包括 store、localstorage 等对象的使用。
父传子
1.通过 props 传参
父组件:
<template>
<Child1 :data="data"></Child1>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import Child1 from './Child1.vue'
export default defineComponent({
components: {
Child1
},
setup() {
return {
data: '测试'
}
}
})
</script>
<style lang="scss" scoped></style>
子组件:
<template>
<div>
{{ data }}
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
props: {
data: {
type: String,
default: () => ''
}
},
setup() {
return {}
}
})
</script>
<style lang="scss" scoped></style>
2.inject and provide
父组件:
<template>
<div>
<Child2></Child2>
</div>
</template>
<script lang="ts">
import { defineComponent, provide, ref } from 'vue'
import Child2 from './Child2.vue'
import { injectSymbol } from './Symbol'
export default defineComponent({
components: {
Child2
},
setup() {
const data = ref('测试2')
provide(injectSymbol, data)
return {}
}
})
</script>
<style scoped></style>
子组件:
<template>
<div>
{{ data }}
</div>
</template>
<script lang="ts">
import { defineComponent, inject } from 'vue'
import { injectSymbol } from './Symbol'
export default defineComponent({
setup() {
const data = inject(injectSymbol)
return {
data
}
}
})
</script>
<style scoped></style>
[!TIP] inject 备注
inject 有三个参数,依次为:1、key or symbol
2、默认值
3、是否使用工厂函数
以下是一个工厂函数的应用场景:
import { inject } from 'vue';
export default {
setup() {
const config = inject('config', () => ({ theme: 'default', fontSize: 14 }), true);
// 子组件可以修改自己的 config 而不会影响其他组件
config.value.theme = 'dark';
// 使用 config...
return { config };
}
}
3.slot 插槽
父组件:
<template>
<div>
<Child3>
<template #main>
{{ data }}
</template>
</Child3>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Child3 from './Child3.vue'
export default defineComponent({
components: {
Child3
},
setup() {
const data = ref('测试3')
return {
data
}
}
})
</script>
<style scoped></style>
子组件:
<template>
<div>
<slot name="main"></slot>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
setup() {
return {}
}
})
</script>
<style scoped></style>
4.使用 ref 获取组件
父组件:
<template>
<div>
<Child4 ref="child"></Child4>
</div>
</template>
<script lang="ts">
import { defineComponent, useTemplateRef, onMounted } from 'vue'
import Child4 from './Child4.vue'
export default defineComponent({
components: {
Child4
},
setup() {
const child: any = useTemplateRef('child')
onMounted(() => {
child.value.data = '测试4'
})
return {}
}
})
</script>
<style scoped></style>
子组件:
<template>
<div>
{{ data }}
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup() {
const data = ref('')
return {
data
}
}
})
</script>
<style scoped></style>
子传父
1.ref
父组件通过 ref 直接获取子组件的实例,本质上操作的同一份数据,既可以理解为父传子,也可以理解为子传父。
2.emit
父组件:
<template>
{{ data }}
<Child1 :data="data" @update-data="updateData"></Child1>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Child1 from './Child1.vue'
export default defineComponent({
components: {
Child1
},
setup() {
const data = ref('测试1')
const updateData = (val: any) => {
data.value = val
}
return {
data,
updateData
}
}
})
</script>
<style lang="scss" scoped></style>
子组件:
<template>
<div>
{{ data }}
<button @click="updateData('修改后的测试1')">修改</button>
</div>
</template>
<script lang="ts">
import { defineComponent, defineEmits } from 'vue'
export default defineComponent({
props: {
data: {
type: String,
default: () => ''
}
},
setup(props, { emit }) {
const updateData = (val: any) => {
emit('update-data', val)
}
return {
updateData
}
}
})
</script>
<style lang="scss" scoped></style>
3.v-model
双向绑定,其实是 props 和 emit 加起来的语法糖。
父组件:
<template>
<div>
{{ data }}
<Child5 v-model="data"></Child5>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Child5 from './Child5.vue'
export default defineComponent({
components: {
Child5
},
setup() {
const data = ref('测试5')
return {
data
}
}
})
</script>
<style scoped></style>
子组件:
<template>
<div>
{{ modelValue }}
<button @click="change('修改后的测试5')">修改</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
props: {
modelValue: {
type: String,
default: () => ''
}
},
setup(props, { emit }) {
const change = (newValue: string) => {
emit('update:modelValue', newValue)
}
return {
change
}
}
})
</script>
<style scoped></style>
4.共享 reactive
reactive 在传输的时候是公用的同一个对象。
而 ref 则会被 Vue 会自动“解包” ref,子组件接收到的是它的 .value 值,如果要实现双向,使用 Ref 声明对象类型。
父组件:
<template>
<div>
<h2>父组件</h2>
<p>计数器:{{ sharedState.count }}</p>
<Child :shared-state="sharedState" />
</div>
</template>
<script setup>
import { reactive } from 'vue'
import Child from './Child.vue'
// 创建 reactive 对象
const sharedState = reactive({
count: 0
})
</script>
子组件:
<template>
<div>
<h3>子组件</h3>
<button @click="sharedState.count++">增加计数器</button>
</div>
</template>
<script setup>
defineProps({
sharedState: Object
})
</script>