Vue-ref与props的使用详解
作者:咖啡の猫
文章总结了Vue3中props和ref的使用方法、特点及区别,包括基本用法、类型校验、默认值设置、事件通信等,并指出了常见误区和最佳实践
一、props:父组件向子组件传递数据
props 是单向数据流的核心,用于父组件向子组件传递数据。
1. 基本用法(Vue 3 +<script setup>)
子组件(ChildComponent.vue)
<script setup>
// 定义接收的 props
defineProps({
title: {
type: String,
required: true
},
count: {
type: Number,
default: 0
}
})
</script>
<template>
<div class="child">
<h3>{{ title }}</h3>
<p>当前计数:{{ count }}</p>
</div>
</template>父组件(ParentComponent.vue)
<script setup>
import ChildComponent from './ChildComponent.vue'
const parentTitle = '来自父组件的数据'
const parentCount = 5
</script>
<template>
<div>
<h1>父组件</h1>
<ChildComponent
:title="parentTitle"
:count="parentCount"
/>
</div>
</template>✅ 数据从父 → 子,单向流动。
2.props的特点
| 特性 | 说明 |
|---|---|
| ✅ 响应式 | 父组件数据变化,子组件自动更新 |
| ❌ 不可直接修改 | 子组件不能修改 props(会报警告) |
| ✅ 类型校验 | 支持 type、required、default |
| ✅ 默认值 | 可设置 default 值 |
错误示例:子组件修改 props
// ❌ 错误!不要这样做 props.count++ // 警告:Avoid mutating a prop directly
✅ 正确做法:通过事件通知父组件修改
<!-- 子组件 -->
<script setup>
const emit = defineEmits(['update-count'])
const increment = () => {
emit('update-count', props.count + 1)
}
</script>
<template>
<button @click="increment">+1</button>
</template><!-- 父组件 --> <ChildComponent :count="count" @update-count="count = $event" />
✅ 实现“子组件通知父组件更新数据”。
二、ref:获取 DOM 元素或子组件实例
ref 是一个响应式引用,可用于:
- 模板中:获取 DOM 元素
- 组件中:获取子组件实例
- 变量:创建响应式数据(Vue 3)
1. 获取 DOM 元素
<script setup>
import { ref, onMounted } from 'vue'
// 创建 ref
const inputRef = ref(null)
onMounted(() => {
// DOM 渲染完成后,inputRef.value 指向真实 DOM
inputRef.value.focus() // 自动聚焦
})
</script>
<template>
<input ref="inputRef" type="text" placeholder="自动聚焦输入框" />
</template>✅ ref 在模板中通过字符串名绑定,在 JS 中通过变量访问。
2. 获取子组件实例
子组件(Modal.vue)
<script setup>
import { ref } from 'vue'
const isVisible = ref(false)
// 提供方法给父组件调用
function open() {
isVisible.value = true
}
function close() {
isVisible.value = false
}
// 暴露给父组件
defineExpose({
open,
close
})
</script>
<template>
<div v-if="isVisible" class="modal">
<p>这是一个模态框</p>
<button @click="close">关闭</button>
</div>
</template>父组件(Parent.vue)
<script setup>
import { ref } from 'vue'
import Modal from './Modal.vue'
const modalRef = ref(null)
const openModal = () => {
modalRef.value.open() // 调用子组件方法
}
</script>
<template>
<div>
<button @click="openModal">打开模态框</button>
<Modal ref="modalRef" />
</div>
</template>✅ 通过 defineExpose 暴露方法,父组件通过 ref 调用。
3.ref创建响应式数据(Vue 3)
<script setup>
import { ref } from 'vue'
const count = ref(0) // ref 包装基本类型
const increment = () => {
count.value++ // 注意:需要 .value
}
</script>
<template>
<p>计数:{{ count }}</p>
<button @click="increment">+1</button>
</template>✅ ref 是 Vue 3 响应式系统的基础之一(与 reactive 并列)。
三、ref与props的对比
| 维度 | props | ref |
|---|---|---|
| 作用 | 父 → 子 传递数据 | 获取 DOM / 组件实例 / 创建响应式变量 |
| 方向 | 自上而下(单向) | 自下而上(调用子组件)或内部使用 |
| 响应式 | 是(父变子变) | 是(ref 变量本身是响应式) |
| 可修改性 | 子组件不能直接修改 | 可以修改(如 count.value++) |
| 使用场景 | 数据传递 | DOM 操作、方法调用、响应式变量 |
| Vue 2 写法 | this.$refs.xxx | this.$refs.xxx |
| Vue 3 写法 | defineProps() | ref() + 模板绑定 |
一句话总结:
props 用于“传数据”,ref 用于“拿实例”或“做响应式变量”。
四、Vue 2 vs Vue 3 差异
Vue 2 写法
// 子组件
export default {
props: ['title'],
mounted() {
this.$refs.input.focus()
}
}
// 父组件
export default {
methods: {
callChild() {
this.$refs.childModal.open()
}
}
}Vue 3<script setup>写法
<script setup> // 使用 defineProps 和 ref const props = defineProps(['title']) const inputRef = ref(null) </script>
✅ Vue 3 更简洁,但需注意 ref 在 setup 中的 .value 问题。
五、常见误区与最佳实践
❌ 误区 1:在setup中直接使用ref变量不加.value
// ❌ 错误
const count = ref(0)
console.log(count) // RefImpl { value: 0 }
// ✅ 正确
console.log(count.value) // 0❌ 误区 2:子组件直接修改props
// ❌ 错误
props.count++
// ✅ 正确:通过事件通知父组件
emit('update:count', props.count + 1)✅ 最佳实践
props只用于传递数据,不用于方法调用ref用于需要直接操作的场景(如聚焦、动画、第三方库集成)- 避免过度使用
ref调用子组件方法,优先使用emit事件通信 - 复杂状态管理使用
Pinia或Vuex,而非层层props传递
六、总结
| API | 核心用途 | 是否响应式 | 是否可修改 | 推荐场景 |
|---|---|---|---|---|
| props | 父 → 子 传数据 | ✅ | ❌(子不能改) | 配置项、展示数据 |
| ref | 获取实例 / 响应式变量 | ✅ | ✅ | DOM 操作、方法调用、局部状态 |
通信原则:
- 数据流:
props向下,emit向上 - 实例调用:
ref获取子组件,谨慎使用 - 响应式:
ref和reactive是 Vue 3 的基石
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
