[Vue3]父子组件相互传值数据同步

本文详细介绍了Vue3中如何使用setup语法糖进行父子组件间的值传递,包括props、toRef、computed、emit和expose等方法,以及v-model的双向绑定机制,强调了官方推荐实践的重要性。
摘要由CSDN通过智能技术生成

简介

vue3中使用setup语法糖,父子组件之间相互传递数据及数据同步问题


父传子

props传递值 使用v-bind绑定

父组件通过props给子组件传递值,props传递的值在子组件中无法修改

// 父组件
<template>
	<div style="color: red">
		 我是父组件
		 <Child :msg="msg"></Child>
	</div>
</template>
 
<script setup>
    import Child from './Child.vue';
    import { ref } from 'vue';
    const msg = ref('111');
</script>
// 子组件
<template>
  <div style="color: blue">
    <div>我是子组件, 父组件传递来的值是{{msg}}</div>
  </div>
</template>

<script setup>
	defineProps({
	  msg: String,
	})
</script>

在这里插入图片描述

props需要计算

子组件获取props后,需要显示根据其计算后的值,而props是无法修改的。

toRef

props是具有响应式的,普通的解构和取值会失去响应式,所以下面这种情况只能取到props的初始值,如果props变化子组件不会更新:

// 父组件
<script setup>
  import Child from './Child.vue';
  import { ref } from 'vue';
  const msg = ref('111');
</script>

<template>
  <div style="color: red">
    我是父组件
    {{ msg }}
    <Child :msg="msg"></Child>
    <button @click="() => msg = '222'">父组件点击改变props值</button>
  </div>
</template>
// 子组件
<script setup>
  import { ref } from 'vue'
  const props = defineProps({
    msg: String,
  })
  const a = ref(props.msg);
</script>

<template>
  <div style="color: blue">
    <div>我是子组件, 父组件传递来的值是{{a}}</div>
  </div>
</template>

点击按钮后,父组件传递的props改变,但是子组件接收到的却不变。
如果父组件中props没有赋初始值,在子组件中接收到的会是undefined。这是因为 ref 是对传入数据的拷贝,但 toRef 是对传入数据的引用。

// 子组件
  import { toRef } from 'vue'
  const props = defineProps({
    msg: String,
  })
  const a = toRef(props, 'msg');
  // const { msg } = toRefs(props); // 使用toRefs也可以

在这里插入图片描述

computed

根据props修改可以直接使用计算属性

// 父组件
<script setup>
  import Child from './Child.vue';
  import { ref } from 'vue';
  const msg = ref('A')
</script>

<template>
  <div style="color: red">
    我是父组件
    {{ msg }}
    <Child :msg="msg"></Child>
    <button @click="() => msg = 'B'">父组件点击改变props值</button>
  </div>
</template>
// 子组件
<script setup>
  import { computed } from 'vue'
  const props = defineProps({
    msg: String,
  })
  const a = computed(() => props.msg.trim().toLowerCase())
</script>

<template>
  <div style="color: blue">
    <div>我是子组件, 父组件传递来的值是{{ a }}</div>
  </div>
</template>

在这里插入图片描述

emit传递方法 使用v-on绑定

vue3中子组件想调用父组件传递的方法,需要使用defineEmits

// 父组件
<script setup>
  import { ref } from 'vue';
  import Child from './Child.vue';
  const a = ref('1');
  const handleTest = () => {
    a.value = 'change';
  }
</script>

<template>
  <div style="color: red">
    我是父组件
    <Child :msg="a" @test="handleTest"></Child>
  </div>
</template>
// 子组件
<script setup>  
  defineProps(['msg']);
  const emit = defineEmits(["test"])
  const handleClick = () => {
    emit("test")
  }
</script>

<template>
  <div style="color: blue">
    <div>我是子组件{{ msg }}</div>
    <button @click="handleClick">子组件调用父组件方法</button>
  </div>
</template>

在这里插入图片描述

emit的第一个参数是事件名,第二个参数是传递的参数。
这里如果父组件没有传递这个函数,也不会报错。

子传父

expose

父组件展示子组件的数据与方法,子组件需要通过defineExpose将自己的值暴露出来,父组件通过子组件上的ref取到其值

// 父组件
<script setup>
  import { ref, onMounted } from 'vue';
  import Child from './Child.vue';
  const a = ref('1');
  const x = ref();
  onMounted(()=>{
    a.value = x.value.message
  })
  const handle = () => {
    x.value.handleMessage();
  }
</script>

<template>
  <div style="color: red">
    我是父组件
    <Child ref="x"></Child>
    {{ a }}
    <button @click="handle">点击子组件触发事件</button>
  </div>
</template>
<script setup>
  import { ref } from "vue";
  const a = ref('我的值是1')
  const message = ref('我是子组件暴露的值');
  const handleMessage = () => {
    a.value = 'Change';
  }
  defineExpose({
    message,
    handleMessage
  });
</script>

<template>
  <div style="color: blue">
    我是子组件{{ a }}
  </div>
</template>

在这里插入图片描述
父组件调用子组件expose的值,一定要在onMounted之后,否则子组件没有完全加载,取不到值。
这种方法,如果子组件的值修改了,那么父组件也是拿不到的。

v-model

使用v-model可以实现父子组件之间值的同步。

// 父组件
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'

const msg = ref('Hello World!')
</script>

<template>
  <h1>{{ msg }}</h1>
  <Child v-model="msg" />
</template>
// 子组件
<script setup>
const model = defineModel()
</script>

<template>
  <span>My input</span> <input v-model="model">
</template>

在输入框中输入值,上面也会更新
defineModel是一个编译宏,它相当于:

// 子组件
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

<template>
  <span>My input</span><input
    :value="props.modelValue"
    @input="emit('update:modelValue', $event.target.value)"
  />
</template>

所以 v-model如果没有另外声明,实际是给子组件设置了一个名为modelValue的props,一个update:modelValue的emit(注意注意,emit调用时第一个参数一定要跟define时相同,否则找不到,update:后面也不要加空格)。

v-model其实相当于v-bindv-on的组合:

<template>
  <h1>{{ msg }}</h1>
  <Child :modelValue="msg" @update:modelValue="val => msg= val"></Child>
</template>

v-model会将modelValue这个props和update:modelValue这个event绑定在一起。当子组件的值发生变化时,会触发update:modelValue 事件传递给父组件,父组件接收到事件后会更新自己的值并重新渲染子组件;当父组件的值发生变化时,会通过modelValue传递给子组件,子组件接收到 prop 后会更新自己的值并重新渲染。这样就实现了父子组件之间的数据同步。

v-model可以写成v-model:自定义='自定义',那么更新的值就是自定义,更新的函数就是 update:自定义

总结

  • 父传子:definePropsdefineEmits
  • 子传父:defineExpose
  • 双向绑定:v-bind

vue中父子传值的方法还是非常多的,但是其中不乏各种坑,新手还是应该老老实实用官方推荐,否则真的很难不踩坑,太灵活了有些时候也是一种问题呢(无语笑)。

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值