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

简介

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

文章目录

  • 简介
  • 父传子
    • props传递值 使用v-bind绑定
      • props需要计算
        • toRef
        • computed
    • emit传递方法 使用v-on绑定
  • 子传父
    • expose
  • v-model
  • 总结


父传子

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变化子组件不会更新:

// 父组件
<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没有赋初始值,在子组件中接收到的会是undifined。这是因为 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>

[Vue3]父子组件相互传值数据同步_第1张图片
父组件调用子组件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>

[Vue3]父子组件相互传值数据同步_第2张图片
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中父子传值的方法还是非常多的,但是其中不乏各种坑,新手还是应该老老实实用官方推荐,否则真的很难不踩坑,太灵活了有些时候也是一种问题呢(无语笑)。

你可能感兴趣的:(Vue,vue.js)