双向绑定核心是建立“数据↔元素值”的可追踪连接,通过Proxy/Object.defineProperty劫持数据、监听DOM事件实现自动同步。Vue 2用defineProperty,Vue 3用Proxy,React主张单向流,Svelte编译时处理。
JavaScript 中的数据绑定,尤其是双向绑定,核心在于让视图(UI)和数据模型(Model)自动同步:数据变,界面跟着更新;用户操作界面,数据也实时更新。它不是 JavaScript 本身的语法特性,而是通过语言能力(如属性拦截、事件监听、模板解析等)模拟出来的行为。
双向绑定 = 数据劫持(响应式) + 视图更新 + 输入事件绑定。关键不在“双向”,而在于建立“数据 ↔ 元素值”之间的可追踪连接。
Object.defineProperty(Vue 2)或 Proxy(Vue 3、Solid 等)拦截对数据的读取和赋值,一旦赋值就触发更新逻辑、、)的 value 属性,并监听 input、change 等事件以下是一个仅支持 的最小实现,不含模板编译和依赖收集,但体现核心链路:
// 1. 响应式数据
const data = { msg: 'hello' };
const reactiveData = new Proxy(data, {
set(target, key, value) {
target[key] = value;
// 2. 数据变了,更新所有绑定该字段的 input
document.querySelectorAll(`[v-model="${key}"]`).forEach(el => {
el.value = value;
});
return true;
}
});
// 3. 初始化:填充初始值
document.querySelectorAll('[v-model]').forEach(el => {
const key = el.getAttribute('v-model');
el.value = reactiveData[key];
// 4. 绑定输入事件,反向更新数据
el.addEventListener('input', () => {
reactiveData[key] = el.value;
});
});
这个例子没有虚拟 DOM 或依赖追踪,但它清晰展示了“谁改了谁、怎么通知、怎么回写”的主干逻辑。
不同框架用不同策略平衡性能、兼容性和开发体验:
Object.defineProperty 递归遍历 data 对象,为每个属性添加 getter/setter;配合 v-model 指令在编译阶段生成更新函数Proxy,支持新增/删除属性、数组索引赋值、Map/Set 等,响应式更彻底useInput)模拟bind:value 转为双向赋值语句,运行时无代理开销,零运行时响应式系统过度使用可能掩盖数据流向、增加调试难度,尤其在复杂表单或父子组件通信中:
式状态管理(如 Formik、React Hook Form)