双向数据绑定原理
-
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)//示例 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
的行为。
-
//示例 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 通过这个模式来管理组件和数据之间的依赖关系。每当数据变化时,依赖于这些数据的视图(或者是观察者)会被通知并更新
// 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(); // 首次执行以触发getterDep.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树,识别指令(如
-
绑定示例:
function 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的必要部分。
-
参考资料
https://juejin.cn/post/6844903942254510087#heading-9
https://zhuanlan.zhihu.com/p/685914161