Vue3:你和组件之间的“暗号”——响应式和父子通信秘籍!
Vue3:你和组件之间的“暗号”——响应式和父子通信秘籍!
Vue3中的响应式系统和父子组件通信机制是前端开发中的重要知识点。本文将通过生动的比喻和详细的代码示例,帮助读者理解Vue3中ref和reactive的区别,以及props、emit和defineExpose在组件通信中的应用。
响应式:让你的数据“活”起来!
ref vs. reactive:一场“简单”与“复杂”的较量
首先,我们来认识一下Vue3的响应式“双雄”:ref和reactive。这俩哥们儿都是让你的数据“动起来”的神器,但用法上却有点小区别。
- ref:简单数据类型的“小管家”
想象一下,你家有个小本本,上面记录着一些简单的数字或文字,比如你的年龄、你今天的卡路里摄入量,这就是ref的用武之地。它专门负责管理像数字、字符串、布尔值这种“简单”的数据类型。
import { ref } from 'vue'
const counter = ref(0); // 初始值是0
// 更新计数器:
counter.value++; // 注意要通过.value访问哦!
小Tips:
ref为什么需要.value?其实它背后偷偷用了Object.defineProperty来实现响应式,这个方法性能高,能精准地“监视”数据的变化。 就像我们小时候用的那种小本本,一有改动,就得一笔一画的记下来。
- reactive:复杂数据类型的“大管家”
如果你的数据不再是简单的“小本本”,而是一个复杂的“家庭档案”,里面有姓名、年龄、爱好等等,那就要请出reactive了。它专门用来管理像对象、数组这种“复杂”的数据类型。
import { reactive } from 'vue'
const state = reactive({
name: 'Alice',
age: 25
});
// 更新状态:
state.name = 'Bob';
state.age++;
小Tips:
reactive背后用了更高级的Proxy代理,可以监听对象所有属性的变化。但Proxy开销比较大,所以说“简单类型”数据用ref,“复杂类型”数据用reactive是最佳选择。 就像家庭档案,内容繁多,需要一个更专业的“管家”来管理。
总结一下:
特性 | ref | reactive |
---|---|---|
用途 | 处理简单数据类型(数字、字符串、布尔值等) | 处理复杂数据类型(对象、数组等) |
访问方式 | .value | 直接访问属性 |
原理 | Object.defineProperty | Proxy |
性能 | 较好 | 相对开销大 |
父子组件通信:跨越组件的“爱”
接下来,我们聊聊组件之间如何“眉来眼去”——也就是父子组件之间的通信。
props:爸爸给儿子的“零花钱”
父组件可以通过props向子组件传递数据,就像爸爸给儿子零花钱一样,这是单向的数据流,子组件不能直接修改props的值,必须“请求”父组件的“同意”才能修改数据。
父组件(Parent.vue):
<template>
<div>
<h2>父组件</h2>
<ChildComponent :title="title"/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './components/childComponent.vue'
const title = ref("来自父组件的消息")
</script>
子组件 (ChildComponent.vue):
<template>
<div>
<h2>{{ title }}</h2>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
defineProps({
title:{
type:String, // 指定类型
required:true // 必填项
}
})
</script>
小Tips: 子组件必须用defineProps来声明它要接收哪些props,就像你跟爸爸要零花钱,得先说清楚你要多少、干什么用。 而且props里面的属性可以定义类型、是否是必填项,这样可以提高代码的可读性和健壮性!
emit + 自定义事件:儿子给爸爸的“小报告”
光有“零花钱”可不行,儿子还得时不时向爸爸汇报情况。这时就要用到emit和自定义事件了。子组件用emit发送消息,父组件用@监听并处理,就像儿子发微信给爸爸汇报情况。
子组件 (ChildComponent.vue):
<template>
<div>
<h2>{{ title }}</h2>
<button @click="sentMessageToParent">发送信息给父组件</button>
</div>
</template>
<script setup>
import {
defineProps,
defineEmits,
ref
} from 'vue';
const chileMessage=ref('Hello Parent!')
defineProps({
title:{
type:String,
required:true
}
})
const emit = defineEmits(['child-message'])
const sentMessageToParent = () =>{
emit('child-message',chileMessage.value)
}
</script>
父组件 (Parent.vue):
<template>
<div>
<h2>父组件</h2>
<p>来自子组件的消息:{{ messageFromChild }}</p>
<ChildComponent @child-message="handleChildMessage" :title="title"/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './components/childComponent.vue'
const title = ref("来自父组件的消息")
const messageFromChild = ref("")
const handleChildMessage = (message) =>{
console.log('子组件发送消息',message);
messageFromChild.value = message;
}
</script>
小Tips:
defineEmits(['child-message'])就像子组件向父组件“备案”,声明自己会发出一个叫child-message的事件。然后,用emit('child-message', message)发出消息,并在父组件中用@child-message="handleChildMessage"监听这个事件。
handleChildMessage就是父组件接收到消息的处理函数。这里要特别注意,事件名称要保持一致,这样父子组件才能成功通信!
子组件暴露属性和方法
有时候,父组件不仅想接收子组件的消息,还想直接调用子组件的属性或方法。 这时,我们就可以用到defineExpose了。
子组件(Child.vue):
<template>
<div>
Child
</div>
</template>
<script setup>
import { defineExpose } from 'vue'
defineExpose({
childName:'这是子组件的属性',
someMetthod(){
console.log("这是子组件的方法");
}
})
</script>
父组件(Parent.vue):
<template>
<div>
<button @click="handlerClick">按钮</button>
</div>
</template>
<script setup>
import Child from './child.vue'
import {
ref
} from 'vue'
const comp = ref(null) // 标记一个DOM元素 null 组件还没有挂载,DOM也不在
const title=ref('hello') // 标记一个普通变量
const handlerClick = () =>{
console.log(comp.value);
console.log(title.value);
comp.value.someMetthod()
}
</script>
小Tips: 在父组件中,我们需要使用ref来获取子组件的实例(这也是ref一个功能),才能访问子组件暴露出来的属性和方法。 使用 ref 获取子组件实例,需要给子组件添加 ref 属性。
在这里我们可以清楚看到,当ref绑定的是一个数组对象时它的value值是一个Proxy代理对象,我们可以直接通过这个代理对象来操作和使用子组件暴露出来的元素和方法。
总结一下:
通信方式 | 传递方向 | 功能 |
---|---|---|
props | 父 -> 子 | 父组件向子组件传递数据 |
emit + 自定义事件 | 子 -> 父 | 子组件向父组件发送消息 |
defineExpose | 子 -> 父 | 父组件直接访问子组件的属性或方法 |
结尾
好啦,今天的“Vue3 响应式与父子通信”之旅就到这里了!希望你能像“打通任督二脉”一样,彻底搞懂它们! 如果你觉得这篇文章对你有帮助,记得点赞、收藏、分享哦! 咱们下期再见! 🚀✨