Appearance
双向绑定的方式
本节目标:理解“双向绑定的方式”的核心思路,并能把它用于实际开发或面试表达。
- 传统方式:
- 定义属性,定义emit,定义可写状态
- 然后watch属性的变化,更新内部可写状态;
- 同时watch内部状态的变化,当变化时,通过调用emit通知外部
- 计算属性:
- 封装更好.同样定义属性和emit
- 定义计算属性get,set,get从props直接衍生出新值;
- set函数是当值变更时,调用emit,好处是将更新函数和获取函数写在了一起
- defineModel:语法糖
- 不用定义属性和emit
- 当props的对应属性变化时自动更新
- 当值被修改时自动会触发emit通知外部
- 代码最简洁封装最好,但有内外初始值不一致的问题
ai调整版本
- 传统“prop + emit + 本地可写状态 + 双向 watch”的方式
这是历史兼容的做法,但易引入复杂度与循环更新(外部变 -> 内部 watch;内部变 -> 外部 emit,再触发外部回流),维护成本高,应尽量避免作为默认方案。仅在必须维护与外部值“脱钩的临时状态”(如防抖输入、复杂表单草稿)时再考虑。Vue.js 4. “计算属性 getter/setter”封装
适合作为过渡与低成本封装:get 只做纯派生(不可产生副作用),set 只负责对外同步。不要在 getter 里发请求、改状态或做副作用逻辑;必要的值转换放在 setter 或更推荐放到 v-model 修饰符的语义层。Vue.js+1 7. defineModel(推荐,Vue 3.4+) 8. 官方推荐的标准解法:更少样板代码,天然与 v-model 对齐;返回的是一个可写 ref,既能读外部也能写回外部;绑定原生表单控件更直接。Vue.js 9. 多路模型:支持多个 v-model: 目标(如 v-model:title / v-model:visible),各自独立同步,避免自定义事件名分散。Vue.js 10. 修饰符与变换器:不仅能拿到 .trim/.number/.lazy 等修饰符,还可用 get/set 集中做读/写时的值转换(例如大小写规范化、去空格),让转换归位到“模型层”,降低组件内部 watch/计算的负担。Vue.js 11. 类型与选项:可声明必填/默认值/类型,TypeScript 友好。Vue.js
==你提到的“==内外初始值不一致==”——确实是重点坑==
- ==把默认值==下放到父级==(从源头提供),或在子级把模型标记为==必填==以强制父级传值;==
- ==若业务上必须在子级兜底,则在父级初始化阶段显式接管这份默认值(保持源头一致),而不是仅在子里设默认。====Vue.js+1==
官方明确警示==:若在子组件用 defineModel({ default: ... }) 设置默认值、而父组件未提供初值,会出现==子真有值、父仍是 undefined的去同步==。解决思路:==
变更语义与对象可变性
- Vue 传引用:当模型值是对象/数组时,直接在子里深层改属性会绕过预期的“发出变更意图”语义(看起来“没 emit 也变了”)。最佳实践是:将变更语义放在模型写入路径(set/emit),必要时拷贝后再写回,让外部感知一次“原子变更”。(这一点是 v-model 模式设计的初衷)Vue.js+1
- 何时仍然需要本地可写副本 / watch
- 当组件内部需要阶段性脱钩(如输入法合成、草稿态、多步编辑、延迟校验)或要处理跨源头合成值时,可以保留局部状态,但应以 defineModel 为“单一事实来源”:内部完成后,再一次性写回模型,减少来回抖动与多处监听。Vue.js 4. 迁移与兼容要点
- Vue 3 中自定义组件的 v-model 语义已统一为:prop modelValue + 事件 update:modelValue;支持多个 v-model与自定义修饰符。老项目迁移要注意这一断点变化,避免旧式 .sync 与 value/input 约定混用。Vue 3 迁移指南
精简结论
- 默认首选 defineModel:配合修饰符与 get/set 变换,集中处理读写与校验。
- 避免“双向 watch”样式的传统同步;确需临时脱钩再用局部状态。
- 初值一致性由父级负责,别在子里单独给默认值导致去同步。
- 对象模型走“写回”语义(必要时拷贝),不要默默深改引用。
