跨层级的组件数据传递一直没有一个优秀的方法,我也给不出优雅的方法,但是伴随这Vue3的出现好像有了让我觉得更喜欢、更优雅的方式
1.props
// ParentFather
<template>
<FormItem label="姓名">
<Input v-model:value="userName"></Input>
</FormItem>
<Father :userName="userName"></Father>
</template>
<script setup>
import { ref } from 'vue'
import { FormItem, Input } from 'ant-design-vue'
import Father from '@/components/Father.vue'
const userName = ref('')
</script>
// Father
<template>
<Child :userName="userName"></Child>
</template>
<script setup>
import { toRefs } from 'vue'
import Child from '@/components/Child.vue'
const props = defineProps({
userName: String,
})
const { userName } = toRefs(props)
</script>
// Child
<template>
<p>{{ userName }}</p>
</template>
<script setup>
import { toRefs } from 'vue'
const props = defineProps({
userName: String,
})
const { userName } = toRefs(props)
</script>
可以看出是一个三层的组件,其中Father组件接收UserName只是为了传递,我们理解组件其实就是一个函数,现在等同于标识出一个不是自己使用的参数,这样层层传递怎么说算不上优雅
2.provide inject
// ParentFather
<template>
<FormItem label="姓名">
<Input v-model:value="userName"></Input>
</FormItem>
<Father></Father>
</template>
<script setup>
import { provide, ref } from 'vue'
import { FormItem, Input } from 'ant-design-vue'
import Father from '@/components/Father.vue'
const userName = ref('')
provide('userName', userName)
</script>
// Father
<template>
<Child></Child>
</template>
<script setup>
import Child from '@/components/Child.vue'
</script>
// Child
<template>
<p>{{ userName }}</p>
</template>
<script setup>
import { inject } from 'vue'
const userName = inject('userName')
</script>
解决了咱们第一个问题Father组件不必在接收UserName这个prop了,看似好像优雅了些,但是却又出现了另外两个问题
- 数据是从那个组件来的(不可知)
// Father
<template>
<Child></Child>
</template>
<script setup>
import { provide } from 'vue'
import Child from '@/components/Child.vue'
provide('userName', 123)
</script>
2.Child组件看似不需要任何参数 这种隐式的参数,会让组件用起来非常难用,终究成为毒药
3.创新 hook+props
// hook
import { readonly, ref } from 'vue'
const userName = ref('')
export const useUserName = (optional) => {
return {
userName: optional ? readonly(userName) : user
}
}
// ParentFather
<template>
<FormItem label="姓名">
<Input v-model:value="userName"></Input>
</FormItem>
<Father :userName="userName"></Father>
</template>
<script setup>
import { FormItem, Input } from 'ant-design-vue'
import Father from '@/components/Father.vue'
import { useUserName } from '@/components/hook.js'
const { userName } = useUserName()
</script>
// Father
<template>
<Child :userName="userName"></Child>
</template>
<script setup>
import Child from '@/components/Child.vue'
import { useUserName } from '@/components/hook.js'
const { userName } = useUserName(true)
</script>
// Child
<template>
<p>{{ userName }}</p>
</template>
<script setup>
import { toRefs } from 'vue'
const props = defineProps({
userName: String,
})
const { userName } = toRefs(props)
</script>
总结一下优点与缺点吧
优点
1.没有让Father多额外的参数 2.Child组件主动表明自己的prop所需 3.prop相对于inject多了一个readonly的修改错误提示
缺点
1.学习一个新的方式总归有一段时间 2.使用hook根据作用主动需要设置是否可更改const { userName } = useUserName(true) 3.如果Child组件需要更改数据,那还是需要以此emit到数据源(单向数据流 层层传递),这个不是缺点,我个人认为不需要改变,一眼直观看到数据走向