HTML无原生双向绑定,需JS手动实现监听与赋值;Object.defineProperty仅对初始化存在属性生效,新增属性、数组索引赋值不触发更新;Proxy可代理整个对象,支持动态属性与数组,但不兼容IE。

HTML 本身不提供双向绑定能力,所有“动态内容双向绑定”都是 JavaScript 在 DOM 层手动搭建的数据同步通路——不是语法糖,而是你写的监听 + 赋值逻辑。
Object.defineProperty 为什么只能在初始化时用
它只对对象上已存在的属性生效。比如你定义了 data = { name: 'a' },再用 Object.defineProperty(data, 'name', {...}) 是 OK 的;但之后执行 data.age = 25,这个 age 不会触发视图更新,因为没被 define 过。
- 新增字段必须提前声明(哪怕初始值是
undefined) - 数组索引赋值(
arr[0] = 'x')和arr.length = 0同样不会触发,Vue2 内部重写了push/pop等 7 个方法兜底 - 嵌套对象(如
user.profile.city)需递归遍历每一层,否则改city时,profile那层的set根本不执行
Proxy 如何绕过 defineProperty 的限制
Proxy 是对整个对象的代理,不关心字段是否存在、是否新增、是否是数组索引——只要走的是这个代理对象,读写都能捕获。
-
get(target, key)可拦截任意属性读取,适合做依赖收集(比如模板里用了{{ user.name }},就在 get 里记下谁依赖了user.name) -
set(target, key, value)拦截任意赋值,包括obj.xxx = 1和obj['xxx'] = 1,新增属性也自动响应 - 但 IE 完全不支持;对
for...in、Object.keys()等枚举操作,需配合ownKeys和getOwnPropertyDescriptor才能完全代理
v-model 在原生 JS 中怎么手动模拟
它本质是两件事的组合:监听输入事件 + 把新值写回数据,缺一不可。
- 对
和,用input事件(不是change,后者只在失焦时触发) - 监听到变化后,要主动调用类似
setData('msg', e.target.value)的函数,把值写进 Model - 反过来,当 Model 改变时(比如异步加载后),也要主动调用
el.value = data.msg更新 input 的显示 - 如果用
textContent或innerHTML渲染文本节点,记得缓存原始模板字符串(如"{{msg}}"),否则下次渲染会把刚插入的值又当模板再解析一遍
template 标签为什么不能直接写 v-model 或 {{ }}
是原生 HTML 惰性容器,浏览器解析时直接跳过其内容,框架根本“看不到”里面写了什么。
- 写
,Vue 不会解析这行,也不会注册响应式依赖 - 用
document.querySelector('template').content取出节点后直接插入 DOM,绑定丢失、事件不触发 - 正确做法是:把
当纯结构载体,用 JS 克隆节点,再挂载到 Vue 组件的可控范围内(比如vm.$mount()或createApp().mount()),由框架接管后续响应式逻辑
真正容易被忽略的点是:双向绑定从来不是“声明即生效”,而是你得亲手维护数据流向的每一步——监听哪个事件、什么时候更新哪段 DOM、如何避免重复解析模板、怎样处理未定义字段(比如 {{ xxx }})不报错。这些细节不写进代码里,绑定就只是幻觉。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











