双向数据绑定原理
Vue实现数据双向绑定核心原理是MVVM模式
- Model(数据层):Vue中的数据模型,用来存储数据
- View(视图层):Vue中的视图界面,由HTML模板和Vue指令组成
- ViewModel(业务逻辑层):核心,是一个Vue实例,充当Model和View的桥梁
- 数据变化时,通过监听器(Observer)触发依赖通知,更新视图层。
- 用户操作视图时,解析器(Compiler)捕获事件(如
v-model
输入),更新数据层。
双向绑定的实现
监听器
-
用于监听数据变化,方式有数据劫持、发布-订阅模式
-
数据劫持
-
Vue2 :使用
Object.defineProperty
来拦截对象属性的getter
和setter
,在getter中收集依赖,在数据变化时通知视图更新 ,在setter中,通过触发依赖收集中的更新函数,实现视图更新 。(Object.defineProperty() - JavaScript | MDN)js//示例 const o = {}; let bValue = 38; Object.defineProperty(o, "b", { get() { //收集依赖 return bValue; }, set(newValue) { bValue = newValue; // 触发视图更新 }, });
-
Vue3 :使用
Proxy
代替Object.defineProperty
,用于创建一个对象的代理,从而实现基本操作的拦截和自定义,调用get方法时,会进行依赖收集 ,调用set方法时,会进行依赖更新 (Proxy - JavaScript | MDN)-
语法:const p = new Proxy(target, handler)
-
参数:
-
target
要使用
Proxy
包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。 -
handler
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理
p
的行为。
-
js//示例 let products = new Proxy( { browsers: ["Internet Explorer", "Netscape"], }, { get: function (obj, prop) { // 收集依赖 return obj[prop]; }, set: function (obj, prop, value) { obj[prop] = value; // 触发视图更新,表示成功 return true; }, }, );
-
-
-
发布-订阅模式
-
Vue.js 通过这个模式来管理组件和数据之间的依赖关系。每当数据变化时,依赖于这些数据的视图(或者是观察者)会被通知并更新
js// 1. 发布者类 class Dep { constructor() { this.subscribers = []; } // 依赖收集 depend() { if (Dep.target && !this.subscribers.includes(Dep.target)) { this.subscribers.push(Dep.target); } } // 通知更新 notify() { this.subscribers.forEach(sub => sub()); } } // 2. // ①数据劫持 Vue2-Start function defineReactive(obj, key, val) { const dep = new Dep(); Object.defineProperty(obj, key, { get() { dep.depend(); // 收集依赖 return val; }, set(newVal) { val = newVal; dep.notify(); // 触发更新 } }); } // Vue2-End // ②响应式代理 Vue3-Start function reactive(obj) { const deps = new Map(); // 存储每个属性的依赖 return new Proxy(obj, { get(target, key) { let dep = deps.get(key); if (!dep) { dep = new Dep(); deps.set(key, dep); } if (Dep.currentEffect) { dep.depend(Dep.currentEffect); // 收集依赖 } return target[key]; }, set(target, key, value) { target[key] = value; deps.get(key)?.notify(); // 触发更新 return true; } }); } // Vue3-End // 3. 观察者函数 function watch(effect) { Dep.target = effect; // 标记当前依赖 effect(); // 首次执行以触发getter Dep.target = null; // 重置 } // 使用示例 const data = {}; defineReactive(data, 'count', 0); // 订阅数据变化 watch(() => { console.log('Count updated:', data.count); }); // 触发更新 data.count = 1; // 输出: "Count updated: 1"
-
-
Compile 模板解析
-
实现视图到数据的绑定
-
解析阶段
- 遍历DOM树,识别指令(如
v-model
、{{}}
插值)。 - 为每个指令创建对应的更新函数,绑定到数据依赖。
- 遍历DOM树,识别指令(如
-
绑定示例:
jsfunction compileInput(node, data, key) { node.value = data[key]; // 初始化值 node.addEventListener('input', (e) => { data[key] = e.target.value; // View → Model }); // 订阅数据变化,更新视图 watch(key, (value) => node.value = value); // Model → View }
-
虚拟DOM优化
- 将模板转换为轻量级的虚拟DOM树。
- 数据变化时,生成新虚拟DOM,通过Diff算法比对差异,仅更新真实DOM的必要部分。
-
参考资料 juejin.cn/post/684490... zhuanlan.zhihu.com/p/685914161
