文章目录一、标签的 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 元素对象。templatedivinputtypetextrefinputRef/buttonclickfocusInput聚焦输入框/button/div/templatescriptsetupimport{ref,onMounted}fromvue// 声明一个同名的 ref 变量必须和 template 中的 ref 名字完全一致// 用来存储ref标记的内容constinputRefref(null)constfocusInput(){// 通过 .value 访问真实 DOM 并在其上调用原生方法inputRef.value.focus()}/script2. 作用在组件标签上Vue2 vs Vue3 的巨大差异当ref作用在子组件标签上时拿到的是子组件的实例对象。你可以通过它直接调用子组件的方法或访问子组件的数据。Vue3 破坏性重大变化组件的封闭性在 Vue2 中通过this.$refs.child可以无限制地访问子组件的所有数据和方法。在 Vue3script setup中组件默认是关闭Private的这意味着父组件即使拿到了子组件的ref也无法访问其内部的任何变量或方法除非子组件显式暴露。子组件 (Child.vue)必须使用defineExpose暴露允许外部访问的内容。scriptsetupimport{ref}fromvueconstsecretMoneyref(1000)constsayHello(){console.log(Hello from Child!)}// 关键点只有暴露出去的父组件才能通过 ref 拿到defineExpose({sayHello,secretMoney})/script父组件 (Parent.vue)templateChildrefchildRef/buttonclickcallChild调用子组件/button/templatescriptsetupimport{ref}fromvueimportChildfrom./Child.vueconstchildRefref(null)constcallChild(){// 成功调用子组件的方法和数据childRef.value.sayHello()console.log(childRef.value.secretMoney)// 1000}/script二、props 的使用直接把组件看作“公司里的岗位”。父组件是“项目经理PM”子组件是“前端开发RD”props的本质就是经理父组件给开发子组件下发的一份“任务说明书”。它是父组件往子组件传递数据的唯一正规渠道。1. 核心体验任务下发与接收最基础的父传子场景经理父组件手里有一个任务名称和截止日期现在要指派给开发子组件。父组件 (Manager.vue) —— 下发任务父组件在调用子组件时通过自定义属性把数据挂载上去。templatedivclassmanager-boxh2我是经理父组件/h2p当前项目进展正常/phr/DevelopertaskName开发登录页面:days3//div/templatescriptsetupimportDeveloperfrom./Developer.vue// 引入子组件招募开发人员/scriptstylescoped.manager-box{border:3px solid #41b883;padding:20px;border-radius:8px;}/style子组件 (Developer.vue) —— 接受任务子组件使用defineProps就像拿个盘子把接到的任务装起来然后直接在界面上展示。templatedivclassdeveloper-boxh3我是开发子组件/h3p经理分给我的任务是{{ taskName }}/pp要求的开发周期是{{ days }} 天/p/div/templatescriptsetup// 1. 使用 defineProps 明确声明“我能接收哪些任务参数”// 2. 并且规定好它们的类型防止经理瞎传defineProps({taskName:String,// 必须是字符串days:Number// 必须是数字})/scriptstylescoped.developer-box{border:3px solid #35495e;padding:15px;margin-top:15px;background:#f8f9fa;}/style2. 必须死守的底线单向数据流只读单向数据流的意思是任务说明书发下来了开发只能看绝对不能私自涂改如果父组件传过来的数据变了子组件会自动更新但子组件如果尝试去修改propsVue 会直接在控制台报错并拦截。错误篡改示范scriptsetupconstpropsdefineProps({days:Number})constchangeDays(){// 严重错误开发嫌时间太短私自把 3天 改成 10天// 控制台会报错Set operation on key days failed: target is readonly.props.days10}/script遇到需要“修改”的业务场景标准解法场景 A数据只作为初始值子组件接下来想自己独立控制。解法在子组件内部定义一个自己的ref变量把prop的值复制一份存到本地。scriptsetupimport{ref}fromvueconstpropsdefineProps({days:Number})// 正确复制一份副本存到自己兜里myDaysconstmyDaysref(props.days)constextendTime(){// 接下来只改 myDays和经理下发的原始数据没有任何关系了myDays.value10}/script场景 B原始数据需要加工后再展示。解法使用计算属性 (computed) 包装后再用。scriptsetupimport{computed}fromvueconstpropsdefineProps({days:Number})// 正确根据经理给的时间自动计算出换算成小时的数据consttotalHourscomputed(()props.days*24)/script3. 防御性编程类型校验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: []}}})引用类型的“隐蔽隐患”如果经理父组件传过来的是一整个对象或数组// 父组件中的数据constprojectref({id:1,status:未开始})当子组件拿到后如果执行了props.project.status 已完成Vue 此时是不会在控制台报错的原因因为对象的内存地址没有变Vue 没办法轻易拦截。代价这样做会直接污染和篡改父组件里的原始数据破坏了单向数据流。导致以后出了 Bug你根本分不清是经理改的还是开发私自改的。死记一句话只要传的是对象哪怕 Vue 不报错也绝对不要在子组件里直接改它的属性如果要改必须通过$emit派发事件让父组件自己改。总结怎么传父组件在标签上用:属性名值。怎么接子组件在script setup里用defineProps({ ... })。加不加冒号传变量、数字、布尔值、数组对象必须加冒号传死字符串不用加。能不能改只准看不准改非要改就复制成副本地本操作。