个人杂谈2-Vue3组件的设计之跨层级组件间数据传递,到底怎么办?提供一个新的思路

100 阅读2分钟

跨层级的组件数据传递一直没有一个优秀的方法,我也给不出优雅的方法,但是伴随这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了,看似好像优雅了些,但是却又出现了另外两个问题

  1. 数据是从那个组件来的(不可知)
// 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到数据源(单向数据流 层层传递),这个不是缺点,我个人认为不需要改变,一眼直观看到数据走向