双向数据绑定通过Object.defineProperty(Vue 2)或Proxy(Vue 3)劫持数据读写,配合依赖收集与派发更新实现数据与视图自动同步,并通过监听input等事件将用户操作映射回数据更新。
JavaScript 实现双向数据绑定,核心在于建立数据与视图之间的自动同步关系:当数据变化时,视图自动更新;当用户操作视图(如输入框内容改变),数据也同步更新。这并非 JavaScript 原生能力,而是通过语言特性(如 Object.defineProperty、Proxy)配合事件监听与模板解析等机制模拟出来的。
该方法可劫持对象属性的读取(get)和设置(set)操作,在其中插入响应式逻辑:
defineProperty 重定义其 get 和 set。在 get 中收集依赖(如当前正在渲染的 DOM 节点),在 set 中触发更新函数。{{ message }}),就将当前的 Watcher(观察者,封装了更新 DOM 的逻辑)加入该列表。set 触发,遍历依赖列表,调用每个 Watcher 的 update() 方法,进而重新渲染对应视图。局限:无法监听新增/删除属性、数组索引赋值(如 arr[0] = 1)、或非对象类型(如字符串、数字原始值)。
Proxy 是 ES6 提供的代理对象,能拦截整个对象的操作,比 defineProperty 更强大、更简洁:
get)、设置(set)、删除(deleteProperty)、in 操作、数组 length 修改、甚至 for...in 遍历等。push、pop 等方法,无需额外重写原型方法。注意:Proxy 不兼容 IE,需考虑浏览器支持场景。
仅监听数据变化不够,还需把 DOM 事件(如 input、change)映射回数据更新:
v-model 或类似指令,为对应表单元素绑定 input 事件(textarea、input[type="text"])或 change(checkbox、select)。obj.message = $event.target.value),从而触发上面所述的响应式更新链路。value 或 checked 属性,保证“数据 → 视图”通路完整。以下是一个极简但可运行的双向绑定示意:
const data = { message: 'Hello' };
const pr
oxy = new Proxy(data, {
set(obj, key, newVal) {
obj[key] = newVal;
document.getElementById('msg').textContent = newVal;
document.getElementById('input').value = newVal;
return true;
}
});
// 初始化视图
document.getElementById('msg').textContent = data.message;
document.getElementById('input').value = data.message;
// 绑定输入事件
document.getElementById('input').addEventListener('input', e => {
proxy.message = e.target.value; // 触发 Proxy.set
});
实际框架(如 Vue、React(配合 useState + onChange))在此基础上封装了模板编译、依赖追踪、异步队列、组件隔离等能力,使开发体验更自然可靠。