当前位置: 首页 > article >正文

Vue3 ref与props

ref 属性 与 props

一、核心概念对比

特性ref (标签属性)props
作用对象DOM 元素/组件实例组件间数据传递
数据流向父组件访问子组件/DOM父组件 → 子组件
响应性直接操作对象单向数据流(只读)
使用场景获取 DOM/调用子组件方法组件参数传递
Vue3 变化不再自动数组形式需要 defineProps 声明

二、ref 属性的深度解析

1. 基础用法
<!-- 获取 DOM 元素 -->
<template><input ref="inputRef" type="text"><ChildComponent ref="childRef" />
</template><script setup>
import { ref, onMounted } from 'vue'// DOM 引用
const inputRef = ref(null)// 组件引用
const childRef = ref(null)onMounted(() => {inputRef.value.focus() // 操作 DOMchildRef.value.sayHello() // 调用子组件方法
})
</script>
2. 组件引用规范
// 子组件 ChildComponent.vue
<script setup>
// 必须暴露方法才能被父组件调用
defineExpose({sayHello: () => console.log('Hello from child!'),childData: ref('子组件数据')
})
</script>
3. 类型安全(TypeScript)
// 父组件中定义组件引用类型
import ChildComponent from './ChildComponent.vue'const childRef = ref<InstanceType<typeof ChildComponent>>()

三、props 的响应式处理

1. 基础声明
<!-- 父组件 -->
<ChildComponent :title="parentTitle" /><!-- 子组件 -->
<script setup>
const props = defineProps({title: {type: String,required: true}
})
</script>
2. 保持响应性
// 正确方式:使用 toRef
import { toRef } from 'vue'const title = toRef(props, 'title')// 错误!直接解构会失去响应性
// const { title } = props

四、联合使用场景

场景:表单验证组件
<!-- 父组件 -->
<template><ValidationForm ref="formRef" :rules="validationRules"@submit="handleSubmit"/>
</template><script setup>
const formRef = ref(null)
const validationRules = ref({/* 验证规则 */})// 调用子组件方法
const validateForm = () => {formRef.value.validate()
}
</script>
<!-- 子组件 ValidationForm.vue -->
<script setup>
defineProps(['rules'])const validate = () => {// 执行验证逻辑
}// 暴露方法给父组件
defineExpose({ validate })
</script>

五、核心差异总结

对比维度ref (标签属性)props
数据方向父 → 子(操作子组件/DOM)父 → 子(数据传递)
可修改性可直接修改子组件暴露的内容只读(需通过事件通知父组件修改)
响应式机制直接引用对象需要 toRef 保持响应性
典型应用DOM操作/调用子组件方法组件参数配置
组合式 API通过 ref() 创建引用通过 defineProps 声明

六、最佳实践指南

1. ref 使用原则
  • 最小化暴露:只暴露必要的组件方法/数据

  • 避免直接修改:不要通过 ref 直接修改子组件状态

  • 配合 TypeScript:使用类型定义确保安全访问

2. props 使用规范
  • 只读原则:始终视 props 为不可变数据

  • 响应式转换:使用 toRef 处理需要响应式的 props

  • 明确验证:始终定义 props 的类型验证


七、常见问题解决

Q1: 为什么通过 ref 访问子组件属性得到 undefined

原因:子组件未通过 defineExpose 暴露属性
解决方案

// 子组件
defineExpose({publicMethod: () => {/* ... */}
})
Q2: 如何同时使用 ref 和 v-for
<template><ChildComponent v-for="item in list" :key="item.id":ref="setItemRef"/>
</template><script setup>
const itemRefs = ref([])const setItemRef = el => {if (el) itemRefs.value.push(el)
}
</script>
Q3: 如何类型安全地组合 ref 和 props
// 父组件
import ChildComponent from './ChildComponent.vue'type ChildComponentExpose = {validate: () => boolean
}const childRef = ref<ChildComponentExpose>()// 子组件
defineExpose<ChildComponentExpose>({validate: () => true
})

八、综合应用示例

父组件

<template><UserFormref="userForm":user-data="formData"@submit="handleSubmit"/><button @click="validateForm">验证表单</button>
</template><script setup lang="ts">
import { ref } from 'vue'
import UserForm from './UserForm.vue'type UserFormExpose = {validate: () => booleanresetForm: () => void
}const userForm = ref<UserFormExpose>()
const formData = ref({ name: '', email: '' })const validateForm = () => {if (userForm.value?.validate()) {console.log('表单验证通过')}
}const handleSubmit = (data) => {console.log('提交数据:', data)
}
</script>

子组件 UserForm.vue

<template><form @submit.prevent="submitForm"><input v-model="localData.name"><input v-model="localData.email"><button type="submit">提交</button></form>
</template><script setup lang="ts">
import { ref, toRefs } from 'vue'const props = defineProps<{userData: {name: stringemail: string}
}>()const emit = defineEmits(['submit'])// 本地副本(避免直接修改 props)
const localData = ref({ ...props.userData })const validate = () => {return localData.value.name.length > 0 && localData.value.email.includes('@')
}const resetForm = () => {localData.value = { name: '', email: '' }
}const submitForm = () => {emit('submit', localData.value)
}defineExpose({validate,resetForm
})
</script>

关键总结:

  1. ref 属性:用于直接访问 DOM/子组件实例,需要配合 defineExpose 使用

  2. props:用于父组件向子组件传递数据,需保持只读特性

  3. 协作模式

    • 父组件通过 props 传递数据

    • 子组件通过事件通知父组件

    • 必要时通过 ref 调用子组件方法

  4. 类型安全:使用 TypeScript 类型定义确保可靠访问


事件传递

在 Vue3 中,子组件向父组件传递数据主要通过 事件机制 实现。以下是 5 种主要实现方式及其使用场景:


一、基础事件传递 (推荐)

实现方式
子组件触发自定义事件 → 父组件监听事件

<!-- 子组件 ChildComponent.vue -->
<script setup>
const emit = defineEmits(['sendData']) // 声明事件const sendToParent = () => {emit('sendData', { message: 'Hello from child!' }) // 触发事件
}
</script><template><button @click="sendToParent">发送数据</button>
</template>
<!-- 父组件 ParentComponent.vue -->
<template><ChildComponent @send-data="handleData" />
</template><script setup>
const handleData = (payload) => {console.log(payload.message) // 输出:Hello from child!
}
</script>

最佳实践

  • 使用 kebab-case 事件名(如 send-data

  • 通过 TypeScript 定义事件类型:

    const emit = defineEmits<{(e: 'sendData', payload: { message: string }): void
    }>()

二、v-model 双向绑定 (表单场景推荐)

实现原理
v-model 是 :modelValue + @update:modelValue 的语法糖

<!-- 子组件 InputComponent.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])const updateValue = (e) => {emit('update:modelValue', e.target.value)
}
</script><template><input :value="modelValue" @input="updateValue">
</template>
<!-- 父组件 -->
<template><InputComponent v-model="inputValue" />
</template><script setup>
const inputValue = ref('')
</script>

多 v-model 绑定

<ChildComponent v-model:name="userName"v-model:age="userAge"
/>

三、异步回调模式 (需要返回值时)

适用场景:需要等待父组件处理结果的异步操作

<!-- 子组件 -->
<script setup>
const emit = defineEmits(['request'])const handleClick = async () => {const response = await emit('request', { id: 123 })console.log('父组件返回:', response)
}
</script>
<!-- 父组件 -->
<template><ChildComponent @request="handleRequest" />
</template><script setup>
const handleRequest = async (payload) => {const data = await fetchData(payload.id)return data // 返回给子组件
}
</script>

四、Expose 方法调用 (需要直接访问子组件)

<!-- 子组件 -->
<script setup>
const childData = ref('子组件数据')defineExpose({getData: () => childData.value,updateData: (newVal) => childData.value = newVal
})
</script>
<!-- 父组件 -->
<template><ChildComponent ref="childRef" />
</template><script setup>
const childRef = ref(null)const getChildData = () => {console.log(childRef.value?.getData()) // 输出:子组件数据childRef.value?.updateData('新数据')
}
</script>

五、状态管理方案 (跨组件通信)

适用场景:多层嵌套组件或兄弟组件通信

// store/counter.js
import { reactive } from 'vue'export const counterStore = reactive({count: 0,increment() {this.count++}
})
<!-- 子组件 -->
<script setup>
import { counterStore } from './store/counter'const updateCount = () => {counterStore.increment()
}
</script>
<!-- 父组件 -->
<script setup>
import { counterStore } from './store/counter'
</script><template>当前计数:{{ counterStore.count }}
</template>

方法对比表

方法适用场景优点缺点
基础事件传递简单数据传递直观明确多层嵌套时需逐层传递
v-model 绑定表单输入组件语法简洁仅适用简单双向绑定
异步回调模式需要父组件响应结果支持异步交互逻辑复杂度稍高
Expose 方法需要直接操作子组件灵活性强破坏组件封装性
状态管理跨组件/复杂场景解耦组件关系需要额外学习成本

最佳实践指南

  1. 优先使用事件传递:保持组件独立性

  2. 复杂场景用状态管理:Pinia 是 Vue3 官方推荐方案

  3. v-model 用于表单:保持双向绑定的简洁性

  4. 避免滥用 ref:防止组件过度耦合

  5. TypeScript 类型定义

    // 事件类型定义
    defineEmits<{(e: 'updateData', payload: { id: number }): void(e: 'cancel'): void
    }>()// Props 类型定义
    defineProps<{userId: numberuserName: string
    }>()

完整示例:购物车组件交互

<template><div class="cart-item"><span>{{ item.name }}</span><input type="number" :value="item.quantity"@input="updateQuantity($event.target.value)"><button @click="emit('remove', item.id)">删除</button></div>
</template><!-- 子组件 CartItem.vue -->
<script setup>
const props = defineProps({item: {type: Object,required: true}
})const emit = defineEmits(['update:quantity', 'remove'])const updateQuantity = (newQty) => {emit('update:quantity', {id: props.item.id, qty: newQty})
}
</script>
<template><CartItem v-for="item in cartItems":key="item.id":item="item"@update:quantity="handleUpdate"@remove="handleRemove"/>
</template><!-- 父组件 ShoppingCart.vue -->
<script setup>
const cartItems = ref([{ id: 1, name: '商品A', quantity: 2 },{ id: 2, name: '商品B', quantity: 1 }
])const handleUpdate = ({ id, qty }) => {const item = cartItems.value.find(i => i.id === id)if (item) item.quantity = Number(qty)
}const handleRemove = (id) => {cartItems.value = cartItems.value.filter(i => i.id !== id)
}
</script>

相关文章:

Vue3 ref与props

ref 属性 与 props 一、核心概念对比 特性ref (标签属性)props作用对象DOM 元素/组件实例组件间数据传递数据流向父组件访问子组件/DOM父组件 → 子组件响应性直接操作对象单向数据流&#xff08;只读&#xff09;使用场景获取 DOM/调用子组件方法组件参数传递Vue3 变化不再自…...

UML设计系列(9):开发过程中如何应用UML

传送门 UML设计系列(1)&#xff1a;状态机图 UML设计系列(2)&#xff1a;类图 UML设计系列(3)&#xff1a;时序图 UML设计系列(4)&#xff1a;用例图 UML设计系列(5)&#xff1a;系统依赖图 UML设计系列(6)&#xff1a;活动图 UML设计系列(7)&#xff1a;UML设计阶段性总…...

Linux之安装配置Nginx

Linux系统下安装配置Nginx的详细步骤如下&#xff1a; 一、准备工作 系统环境&#xff1a;确保Linux系统已安装&#xff0c;并且具有网络连接&#xff08;以便在线安装依赖或下载Nginx&#xff09;。 安装依赖&#xff1a;Nginx依赖于一些开发库和工具&#xff0c;如gcc、pcr…...

【C++】STL之deque

deque Deque 的底层既不直接依赖 vector 也不依赖 list&#xff0c;而是结合了两者的思想&#xff0c;采用了一种分块&#xff08;chunk&#xff09;存储与动态指针数组&#xff08;map&#xff09;结合的结构。以下是详细分析&#xff1a; 1. 底层结构设计 Deque 的核心设计…...

模板方法模式:定义算法骨架的设计模式

模板方法模式&#xff1a;定义算法骨架的设计模式 一、模式核心&#xff1a;模板方法定义算法骨架&#xff0c;具体步骤延迟到子类实现 在软件开发中&#xff0c;经常会遇到这样的情况&#xff1a;某个算法的步骤是固定的&#xff0c;但具体步骤的实现可能因不同情况而有所不…...

通付盾入选苏州市网络和数据安全免费体验目录,引领企业安全能力跃升

近日&#xff0c;苏州市网络安全主管部门正式发布《苏州市网络和数据安全免费体验产品和服务目录》&#xff0c;通付盾凭借其在数据安全、区块链、AI领域的创新实践和前沿技术实力&#xff0c;成功入选该目录。 作为苏州市网络安全技术支撑单位&#xff0c;通付盾将通过 “免费…...

【金仓数据库征文】加速数字化转型:金仓数据库在金融与能源领域强势崛起

目录 一、引言 二、金仓数据库&#xff08;KingbaseES&#xff09;概述 1. 发展历程与市场地位 2. 核心技术架构 3. 金仓数据库的特点 三、金仓数据库在金融行业的应用 1. 金融行业的挑战与需求 2. 金仓数据库在金融行业的优势 3. 金仓数据库在金融行业的实际应用案例 …...

音频base64

音频 Base64 是一种将二进制音频数据&#xff08;如 MP3、WAV 等格式&#xff09;编码为 ASCII 字符串的方法。通过 Base64 编码&#xff0c;音频文件可以转换为纯文本形式&#xff0c;便于在文本协议&#xff08;如 JSON、XML、HTML 或电子邮件&#xff09;中传输或存储&#…...

Qt C++/Go/Python 面试题(持续更新)

目录 1、封装、继承、多态是什么&#xff1f; 2、final标识符的作用是什么&#xff1f; 3、介绍一下虚函数 4、介绍一下智能指针 5、介绍一下左值、右值、左值引用、右值引用 6、指针和引用有什么区别&#xff1f; 7、define和const的区别是什么&#xff1f; 8、C程序的…...

VMware 虚拟机镜像资源网站

常见的 VMware 虚拟机镜像资源网站 网站名称链接地址特点OSBoxes.orgOSBoxes - Virtual Machines for VirtualBox & VMware 提供 .vmx .vmdk&#xff0c;适合 VMware 和 VirtualBox&#xff0c;更新频率高&#xff0c;界面清晰LinuxVMImages.comLinux VM Images - Downlo…...

C++智能指针上

一、裸指针 “裸指针”是最基础的&#xff0c;直接存储内存地址的指针类型。特点&#xff1a;①它本身没有自动的内存管理机制&#xff1a;如它不会自动释放内存&#xff0c;也不会检查是否指向有效的内存区域&#xff1b;②直接操作内存地址&#xff0c;不进行任何的边界检查&…...

低代码平台开发串口调试助手

项目介绍 串口调试助手是一款用于串口通信调试的工具&#xff0c;它可以帮助开发人员发送和接收串口数据&#xff0c;主要用于嵌入式开发、工业控制、物联网设备开发等领域。 主要功能包括&#xff1a; 数据收发&#xff1a;可以实时发送和接收串口数据&#xff0c;并显示在界…...

怎么配置一个kubectl客户端访问多个k8s集群

怎么配置一个kubectl客户端访问多个k8s集群 为什么有的客户端用token也访问不了k8s集群&#xff0c;因为有的是把~/.kube/config文件&#xff0c;改为了~/.kube/.config文件&#xff0c;文件设置成隐藏文件了。 按照kubectl的寻找配置的逻辑&#xff0c;kubectl找不到要访问集群…...

C语言分支结构详解

一、引言 在 C 语言中&#xff0c;分支结构是程序控制流的重要组成部分。它允许程序根据不同的条件执行不同的代码块&#xff0c;从而实现更灵活和复杂的逻辑。分支结构使得程序能够根据输入、变量的值或其他条件来做出决策&#xff0c;决定程序的执行路径。 二、if 语句 基…...

Redisson实战:分布式系统中的五大典型应用场景

引言 在分布式系统架构中&#xff0c;数据一致性、高并发控制和资源协调是开发者面临的核心挑战。Redisson作为基于Redis的Java客户端&#xff0c;不仅提供了丰富的分布式对象和服务&#xff0c;还简化了分布式场景下的编程模型。本文将通过实际代码示例&#xff0c;解析Redis…...

12N60-ASEMI无人机专用功率器件12N60

编辑&#xff1a;LL 12N60-ASEMI无人机专用功率器件12N60 型号&#xff1a;12N60 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220F 最大漏源电流&#xff1a;12A 漏源击穿电压&#xff1a;600V 批号&#xff1a;最新 RDS&#xff08;ON&#xff09;Max&#xff1a;0.68…...

长城智驾重复造轮子

左手新能源&#xff0c;右手智驾&#xff0c;这是长城当下最在意的两块业务。 从去年8月首款具备高阶智能驾驶功能SUV全新蓝山上市之后&#xff0c;长城在传播端的重点就是围绕智驾、无图方案打造智驾标签。 先是在广州国际车展上&#xff0c;整个展厅只展出全新蓝山&#xf…...

云原生之认识DDD

一、DDD是什么? 领域驱动设计(DDD) 做为一种软件工程的方法论,它可以帮助我们设计高质量的软件,或者说任何工程的设计都需要方法论,不论是城市设计、建筑设计、室内设计。 比如没有方法论的情况下楼是可以盖起来的,或许整个楼道和窗户上挂满了电话线、闭路线、电线?下水…...

continue插件实现IDEA接入本地离线部署的deepseek等大模型

文章目录 前言一、IDEA安装continue二、continue部署本地大模型三、continue聊天窗口使用deepseek R1四、continue批量接入硅基流动的模型API 前言 亲爱的家人们&#xff0c;创作很不容易&#xff0c;若对您有帮助的话&#xff0c;请点赞收藏加关注哦&#xff0c;您的关注是我…...

代码随想录算法训练营第一天:数组part1

今日学习的文章链接和视频链接 ● 自己看到题目的第一想法 ● 看完代码随想录之后的想法 ● 自己实现过程中遇到哪些困难 ● 今日收获&#xff0c;记录一下自己的学习时长 状态 思路理解完成 30% 代码debug完成 60% 代码模板总结并抽象出来 100% 题目 704 二分查找 题目链接…...

滚珠螺杆在数控机床中如何降低摩擦系数?

对数控机床这样要求加工精度高而且加工精度能保持长期稳定的设备来说是必须的&#xff0c;而且具有较低的传动阻力也同时为更高速的传动打下基础。使用滚珠螺杆&#xff0c;也是数控机床加工效率高的一个重要原因&#xff0c;为了减少数控机床的滚珠螺杆出现摩擦&#xff0c;可…...

【现代深度学习技术】循环神经网络05:循环神经网络的从零开始实现

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上&#xff0c;结合当代大数据和大算力的发展而发展出来的。深度学习最重…...

Python实现技能记录系统

Python实现技能记录系统 来自网络&#xff0c;有改进。 技能记录系统界面如下&#xff1a; 具有保存图片和显示功能——允许用户选择图片保存&#xff0c;选择历史记录时若有图片可预览图片。 这个程序的数据保存在数据库skills2.db中&#xff0c;此数据库由用Python 自带的…...

前端基础之《Vue(10)—过滤器》

一、过滤器 1、作用 用于数据处理。 2、全局过滤器 使用Vue.filter(名称, val>{return newVal})定义。 在任何组件中都可以直接使用。 3、局部过滤器 使用选项&#xff0c;filters: {}定义&#xff0c;只能在当前组件中使用。 4、过滤器在Vue 3.0中已经淘汰了 5、过滤器…...

Linux常见指令介绍下(入门级)

1. head head就和他的名字一样&#xff0c;是显示一个文件头部的内容&#xff08;会自动排序&#xff09;&#xff0c;默认是打印前10行。 语法&#xff1a;head [参数] [文件] 选项&#xff1a; -n [x] 显示前x行。 2. tail tail 命令从指定点开始将文件写到标准输出.使用t…...

VIC-3D非接触全场应变测量系统用于小尺寸测量之电子元器件篇—研索仪器DIC数字图像相关技术

在5G通信、新能源汽车电子、高密度集成电路快速迭代的今天&#xff0c;电子元件的尺寸及连接工艺已进入亚毫米级竞争阶段&#xff0c;这种小尺寸下的力学性能评估对测量方式的精度有更高的要求&#xff0c;但传统应变测量手段常因空间尺寸限制及分辨率不足难以捕捉真实形变场。…...

字典与集合——测试界的黑话宝典与BUG追捕术

主题&#xff1a;“字典是测试工程师的暗号手册&#xff0c;集合是BUG的照妖镜” 一、今日目标 ✅ 掌握字典的「键值对暗号体系」与集合的「去重妖法」✅ 开发《测试工程师黑话词典》&#xff0c;让新人秒变老司机✅ 统计自动化测试结果中的高频BUG类型&#xff08;附赠甩锅指…...

下篇:深入剖析 BLE GATT / GAP / SMP 与应用层(约5000字)

引言 在 BLE 协议栈的最上层,GAP 定义设备角色与连接管理,GATT 构建服务与特征,SMP 负责安全保障,应用层则承载具体业务逻辑与 Profile。掌握这一层,可实现安全可靠的设备发现、配对、服务交互和定制化业务。本文将详解 GAP、GATT、SMP 三大模块,并通过示例、PlantUML 时…...

事务详细介绍

一、简介 1、什么是事务 事务是指一组操作&#xff0c;这些操作要么全部成功执行&#xff0c;要么全部不执行&#xff0c;保证数据的完整性和一致性。事务广泛应用于数据库管理系统、分布式系统和企业级应用中&#xff1b; 2、事务的特性 事务具有四个基本特性&#xff0c;…...

PostgreSQL 中的权限视图

PostgreSQL 中的权限视图 PostgreSQL 提供了多个系统视图来查询权限信息&#xff0c;虽然不像 Oracle 的 DBA_SYS_PRIVS 那样集中在一个视图中&#xff0c;但可以通过组合以下视图获取完整的系统权限信息。 一 主要权限相关视图 Oracle 视图PostgreSQL 对应视图描述DBA_SYS_…...