news 2026/6/23 1:27:59

五、Vue3 ref 用法 + Props 完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
五、Vue3 ref 用法 + Props 完整指南

文章目录

    • 一、标签的 ref 属性(模板引用)
      • 1. 作用在原生 HTML 元素上
      • 2. 作用在组件标签上(Vue2 vs Vue3 的巨大差异)
    • 二、props 的使用
      • 1. 核心体验:任务下发与接收(最基础的父传子)
        • 父组件 (`Manager.vue`) —— 下发任务
        • 子组件 (`Developer.vue`) —— 接受任务
      • 2. 必须死守的底线:单向数据流(只读)
        • 错误篡改示范
        • 遇到需要“修改”的业务场景,标准解法:
          • 场景 A:数据只作为初始值,子组件接下来想自己独立控制。
          • 场景 B:原始数据需要加工后再展示。
      • 3. 防御性编程:类型校验(Props Validation)
        • 引用类型的“隐蔽隐患”
      • 总结

一、标签的 ref 属性(模板引用)

在 Vue 中,我们通常不需要直接操作 DOM(因为有响应式系统和虚拟 DOM)。但有些场景(如:聚焦输入框、获取元素宽高、调用子组件方法)必须访问底层 DOM 或子组件实例,这时就要用到ref

1. 作用在原生 HTML 元素上

ref作用在普通 HTML 标签上时,拿到的就是真实的 DOM 元素对象

<template><div><inputtype="text"ref="inputRef"/><button@click="focusInput">聚焦输入框</button></div></template><scriptsetup>import{ref,onMounted}from'vue'// 声明一个同名的 ref 变量(必须和 template 中的 ref 名字完全一致)// 用来存储ref标记的内容constinputRef=ref(null)constfocusInput=()=>{// 通过 .value 访问真实 DOM 并在其上调用原生方法inputRef.value.focus()}</script>

2. 作用在组件标签上(Vue2 vs Vue3 的巨大差异)

ref作用在子组件标签上时,拿到的是子组件的实例对象。你可以通过它直接调用子组件的方法或访问子组件的数据。

Vue3 破坏性重大变化:组件的封闭性

  • 在 Vue2 中:通过this.$refs.child可以无限制地访问子组件的所有数据和方法。

  • 在 Vue3<script setup>:组件默认是关闭(Private)的!这意味着父组件即使拿到了子组件的ref,也无法访问其内部的任何变量或方法,除非子组件显式暴露。

子组件 (Child.vue):必须使用defineExpose暴露允许外部访问的内容。

<scriptsetup>import{ref}from'vue'constsecretMoney=ref(1000)constsayHello=()=>{console.log('Hello from Child!')}// 关键点:只有暴露出去的,父组件才能通过 ref 拿到defineExpose({sayHello,secretMoney})</script>

父组件 (Parent.vue)

<template><Childref="childRef"/><button@click="callChild">调用子组件</button></template><scriptsetup>import{ref}from'vue'importChildfrom'./Child.vue'constchildRef=ref(null)constcallChild=()=>{// 成功调用子组件的方法和数据childRef.value.sayHello()console.log(childRef.value.secretMoney)// 1000}</script>

二、props 的使用

直接把组件看作“公司里的岗位”。

  • 父组件是“项目经理(PM)”

  • 子组件是“前端开发(R&D)”

props的本质,就是经理(父组件)给开发(子组件)下发的一份“任务说明书”。它是父组件往子组件传递数据的唯一正规渠道

1. 核心体验:任务下发与接收(最基础的父传子)

场景:经理(父组件)手里有一个任务名称和截止日期,现在要指派给开发(子组件)。

父组件 (Manager.vue) —— 下发任务

父组件在调用子组件时,通过自定义属性把数据挂载上去。

<template><divclass="manager-box"><h2>我是经理(父组件)</h2><p>当前项目进展:正常</p><hr/><DevelopertaskName="开发登录页面":days="3"/></div></template><scriptsetup>importDeveloperfrom'./Developer.vue'// 引入子组件(招募开发人员)</script><stylescoped>.manager-box{border:3px solid #41b883;padding:20px;border-radius:8px;}</style>
子组件 (Developer.vue) —— 接受任务

子组件使用defineProps就像拿个盘子把接到的任务装起来,然后直接在界面上展示。

<template><divclass="developer-box"><h3>我是开发(子组件)</h3><p>经理分给我的任务是:{{ taskName }}</p><p>要求的开发周期是:{{ days }} 天</p></div></template><scriptsetup>// 1. 使用 defineProps 明确声明:“我能接收哪些任务参数”// 2. 并且规定好它们的类型(防止经理瞎传)defineProps({taskName:String,// 必须是字符串days:Number// 必须是数字})</script><stylescoped>.developer-box{border:3px solid #35495e;padding:15px;margin-top:15px;background:#f8f9fa;}</style>

2. 必须死守的底线:单向数据流(只读)

单向数据流的意思是:任务说明书发下来了,开发只能看,绝对不能私自涂改!如果父组件传过来的数据变了,子组件会自动更新;但子组件如果尝试去修改props,Vue 会直接在控制台报错并拦截。

错误篡改示范
<scriptsetup>constprops=defineProps({days:Number})constchangeDays=()=>{// 严重错误!开发嫌时间太短,私自把 3天 改成 10天// 控制台会报错:Set operation on key "days" failed: target is readonly.props.days=10}</script>
遇到需要“修改”的业务场景,标准解法:
场景 A:数据只作为初始值,子组件接下来想自己独立控制。

解法:在子组件内部定义一个自己的ref变量,把prop的值复制一份存到本地。

<scriptsetup>import{ref}from'vue'constprops=defineProps({days:Number})// 正确:复制一份副本,存到自己兜里(myDays)constmyDays=ref(props.days)constextendTime=()=>{// 接下来只改 myDays,和经理下发的原始数据没有任何关系了myDays.value=10}</script>
场景 B:原始数据需要加工后再展示。

解法:使用计算属性 (computed) 包装后再用。

<scriptsetup>import{computed}from'vue'constprops=defineProps({days:Number})// 正确:根据经理给的时间,自动计算出换算成小时的数据consttotalHours=computed(()=>props.days*24)</script>

3. 防御性编程:类型校验(Props Validation)

为了防止团队协作时队友“胡乱传参”(比如子组件需要数字,队友却传了个对象),我们在子组件写defineProps时要开启严格的拦截校验。这相当于给组件做了一份参数合同

defineProps({// 1. 基础检查:必须是字符串类型taskName:String,// 2. 多类型允许:可以是数字,也可以是字符串id:[String,Number],// 3. 强制要求:父组件必须传这个参数,不传界面直接报错username:{type:String,required:true},// 4. 默认值:如果父组件不传,就自动使用 18age:{type:Number,default:18},// 5. 注意:如果默认值是对象(Object)或数组(Array),必须用函数返回!skills:{type:Array,default(){return['Vue3','Git']// 不能直接写成 default: []}}})
引用类型的“隐蔽隐患”

如果经理(父组件)传过来的是一整个对象数组

// 父组件中的数据constproject=ref({id:1,status:'未开始'})

当子组件拿到后,如果执行了props.project.status = '已完成'Vue 此时是不会在控制台报错的!

  • 原因:因为对象的内存地址没有变,Vue 没办法轻易拦截。

  • 代价:这样做会直接污染和篡改父组件里的原始数据,破坏了单向数据流。导致以后出了 Bug,你根本分不清是经理改的还是开发私自改的。

死记一句话:只要传的是对象,哪怕 Vue 不报错,也绝对不要在子组件里直接改它的属性!如果要改,必须通过$emit派发事件让父组件自己改。

总结

  1. 怎么传:父组件在标签上用:属性名="值"

  2. 怎么接:子组件在<script setup>里用defineProps({ ... })

  3. 加不加冒号:传变量、数字、布尔值、数组对象必须加冒号;传死字符串不用加。

  4. 能不能改:只准看不准改,非要改就复制成副本地本操作。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 1:27:07

2026论文写作工具红黑榜:AI论文网站怎么选?看完少走弯路

红榜优先选千笔AI、ThouPen、豆包&#xff0c;适配国内学术规范&#xff1b;黑榜避开低质免费工具、无真实引用平台、过度依赖全文生成的工具&#xff0c;优先按需求匹配三维模型&#xff08;需求匹配度 - 数据可信度 - 成本承受力&#xff09;。 一、红榜&#xff1a;10 款高分…

作者头像 李华
网站建设 2026/6/8 22:30:37

家庭投资组合方案(2026/6/7版)

文章目录&#x1f3e0; 家庭投资组合方案&#xff08;2026/6/7版&#xff09;一、&#x1f467; 女儿账户&#xff1a;超长期复利罐二、&#x1f469; 媳妇账户&#xff1a;中期稳定器三、&#x1f4ca; 两个账户差异对比四、⚙️ 统一操作纪律&#x1f4c5; 定投规则&#xff…

作者头像 李华
网站建设 2026/6/8 22:27:37

FramePack终极指南:如何用普通显卡创作60秒高清舞蹈视频

FramePack终极指南&#xff1a;如何用普通显卡创作60秒高清舞蹈视频 【免费下载链接】FramePack Lets make video diffusion practical! 项目地址: https://gitcode.com/gh_mirrors/fr/FramePack 你是否曾经梦想用AI创作专业级舞蹈视频&#xff0c;却被昂贵的硬件要求和…

作者头像 李华
网站建设 2026/6/8 22:27:29

RustPython:用 Rust 编写的 Python 3 解释器

文章目录RustPython&#xff1a;用 Rust 编写的 Python 3 解释器RustPython&#xff1a;用 Rust 编写的 Python 3 解释器 用 Rust 编写的 Python 3 解释器 RustPython&#xff0c;在 GitHub 上已经获得了 22,091 个 Star&#xff1a; RustPython 是一个完全用 Rust 实现的 Pyt…

作者头像 李华
网站建设 2026/6/11 10:33:58

pywencai项目Cookie配置终极指南:简单快速获取同花顺问财数据

pywencai项目Cookie配置终极指南&#xff1a;简单快速获取同花顺问财数据 【免费下载链接】pywencai 获取同花顺问财数据 项目地址: https://gitcode.com/gh_mirrors/py/pywencai pywencai是一个专为量化研究和金融分析设计的Python库&#xff0c;能够高效获取同花顺问财…

作者头像 李华
网站建设 2026/6/8 22:25:57

TOPSIS算法实战:如何用它帮你选到最合适的租房/买车方案?

TOPSIS算法实战&#xff1a;如何用它帮你选到最合适的租房/买车方案&#xff1f;刚毕业的小王最近遇到了一个幸福的烦恼&#xff1a;手头有三套条件各异的租房备选方案。A房源月租低但通勤时间长&#xff0c;B小区设施完善但超出预算15%&#xff0c;C公寓户型方正却临近工地。面…

作者头像 李华