vue3组件通信方式---父子互传

274 阅读2分钟

props

经典的属性绑定传参,学过vue就会。

// 核心代码,接收父组件的值
let { item } = defineProps<{ item: any }>()

具体代码

下面是父组件

<script setup lang="ts">
import { reactive } from 'vue'
import Son from './components/Son.vue'
let obj = reactive({
  name: '张三',
  age: 18,
  sex: 'male'
})
</script>

<template>
  <div class="main">
    <Son :item="obj"></Son>
  </div>
</template>

<style lang="less">
body {
  margin: 0;
}
.main {
  height: 300px;
  background-color: skyblue;
  padding: 12px 32px;
}
</style>

下面是子组件

<template>
  <div class="son">
    子元素
    <div class="inner">
      父组件传来的
      <div>姓名: {{ item.name }}</div>
      <div>年龄: {{ item.age }}</div>
      <div>性别: {{ item.sex }}</div>
    </div>
  </div>
</template>
<script setup lang="ts">
let { item } = defineProps<{ item: any }>()
console.log('父传子: ', item)
</script>
<style scoped lang="less">
.son {
  background-color: red;
}
</style>

props子传父

基本一致,子组件使用父组件定义的回调函数传参。
回调传参的思路都是一致的,父组件定义回调函数,用于接收和更新自身的数据;子组件调用该函数并把值作为参数传过去。

核心代码

let item = ref<Item>({} as Item)
let setValue = (obj: Item) => {
  console.log('父组件获取到obj', obj)
  item.value = obj
}

具体代码

下面是父组件

<script setup lang="ts">
import { reactive, ref } from 'vue'
import Son from './components/Son.vue'
interface Item {
  name: string
  age: number
  sex: string
}
let item = ref<Item>({} as Item)
let setValue = (obj: any) => {
  console.log('父组件获取到obj', obj)
  item.value = obj
}
</script>

<template>
  <div class="main">
    <Son :setValue="setValue"></Son>
    子组件传来的:
    <div>姓名: {{ item.name }}</div>
    <div>年龄: {{ item.age }}</div>
    <div>性别: {{ item.sex }}</div>
  </div>
</template>

<style lang="less">
body {
  margin: 0;
}
.main {
  height: 300px;
  background-color: skyblue;
  padding: 12px 32px;
}
</style>

下面是子组件

<template>
  <div class="son">子元素</div>
</template>
<script setup lang="ts">
let { setValue } = defineProps<{ setValue: (obj: any) => any }>()
setValue({
  name: '李四',
  age: 20,
  sex: 'female'
})
</script>
<style scoped lang="less">
.son {
  background-color: red;
}
</style>

v-model双向绑定

原理解释。

<Son v-model="item"></Son>

v-model其实被解释为一个v-bind属性值和一个事件绑定

<Son :modelValue="item" @update:modelValue="item = $event" />

父组件中值发生变化时,v-bind可以使得子组件的值同步更新;而子组件中的值发生变化时,我们只需要触发update:modelValue这个事件,并把对应的值作为参数即可,父组件触发回调item = $event完成值的更新。

image.png

具体代码

父组件

<script setup lang="ts">
import { reactive, ref } from 'vue'
import Son from './components/Son.vue'
let item = ref<{ name: string; age: number; sex: string }>({
  name: '张三',
  age: 18,
  sex: '男'
})
</script>

<template>
  <div class="main">
    父组件的add事件
    <button @click="item.age += 1">年龄+1</button>
    <Son v-model="item"></Son>

    父组件区域的值:
    <div>姓名: {{ item.name }}</div>
    <div>年龄: {{ item.age }}</div>
    <div>性别: {{ item.sex }}</div>
  </div>
</template>

<style lang="less">
body {
  margin: 0;
}
.main {
  height: 300px;
  background-color: skyblue;
  padding: 12px 32px;
}
</style>

子组件

<template>
  <div class="son">
    子元素区域
    <button @click="addAge">年龄+1</button>
    <div>姓名: {{ modelValue.name }}</div>
    <div>年龄: {{ modelValue.age }}</div>
    <div>性别: {{ modelValue.sex }}</div>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue'

let item = ref({})
let { modelValue } = defineProps<{ modelValue: any }>()
const emit = defineEmits(['update:modelValue'])
const addAge = () => {
  modelValue.age += 1
  emit('update:modelValue', modelValue)
}
console.log(item)
</script>
<style scoped lang="less">
.son {
  background-color: red;
}
</style>

$parent$refs(双向绑定)

这两种方式用得很少,但是这个更接近于真正意义上的双向绑定,这里只介绍refs 注意:

  1. refsrefs和parent都是只能在template内使用,因此如果要在ts代码中获取,可以使用方法传值过去。
  2. 不像其他的方式,refs这种方式在组件初始化时获取不到值,因此一般使用单击事件等可以触发。

核心:子组件使用defineExpose来暴露自身内部的值给外部,外部使用ref()接收,这是一个宏函数,不需要使用import来导入。

image.png

image.png

具体代码

<script setup lang="ts">
import { reactive, ref } from 'vue'
import Son from './components/Son.vue'
const item = ref()
const handleBtn = (refs: any) => {
  console.log('传来的item对象', item.value.item)
  console.log('age++:', refs.item.item.age++)
}
</script>

<template>
  <div class="main">
    父组件的add事件
    <button @click="handleBtn($refs)">age+1</button>
    <Son ref="item"></Son>
    初始化获取refs:
    {{ $refs }}
  </div>
</template>

<style lang="less">
body {
  margin: 0;
}
.main {
  height: 300px;
  background-color: skyblue;
  padding: 12px 32px;
}
</style>

子组件

<template>
  <div class="son">
    子元素区域
    <!-- <button @click="addAge">年龄+1</button> -->
    <div>姓名: {{ item.name }}</div>
    <div>年龄: {{ item.age }}</div>
    <div>性别: {{ item.sex }}</div>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
let item = ref<{ name: string; age: number; sex: string }>({
  name: '张三',
  age: 18,
  sex: '男'
})
defineExpose({ item })
console.log(item)
</script>
<style scoped lang="less">
.son {
  background-color: red;
}
</style>