Vue 的双向数据绑定是其核心特性之一,使得数据的变化能够自动吧更新到试图,视图的变化也能自动更新到数据。
vue2 双向数据绑定原理
vue2 的双向数据绑定主要基于 数据劫持 结合 发布-订阅者模式 实现,并且在模版编译过程中完成视图和数据的关联。
1.1 数据劫持
Vue2 使用Object.defineProperty() 方法对数据对象的属性进行劫持,当这些属性的值发生变化的时,会触发相应的 getter和setter方法。
1.2 发布者 - 订阅者模式
-
Dep 类(依赖收集器) :每个数据属性都有一个对应的
Dep实例,用于收集依赖和通知订阅者。当属性的getter方法被调用时,会将当前的订阅者添加到Dep实例的订阅者列表中;当属性的setter方法被调用时,会通知所有订阅者更新。 -
Watcher 类(订阅者) :表示一个订阅者,当数据发生变化时,会调用
update方法更新视图。
1.3. 模板编译
Vue 在实例初始化时会对模板进行编译,将模板中的指令和表达式解析成对应的渲染函数。在编译过程中,会为每个绑定了数据的 DOM 元素创建一个 Watcher 实例,该实例会在数据变化时更新对应的 DOM 元素。
1.4. 整体流程
- 初始化数据:创建 Vue 实例时,传入数据对象,使用
observe函数对数据对象进行劫持。 - 模板编译:对模板进行编译,解析指令和表达式,为每个绑定了数据的 DOM 元素创建
Watcher实例。 - 依赖收集:在
Watcher实例的get方法中,将当前的Watcher实例赋值给Dep.target,然后读取数据属性,触发属性的getter方法,将Dep.target添加到Dep实例的订阅者列表中。 - 数据更新:当数据属性的值发生变化时,触发属性的
setter方法,调用Dep实例的notify方法通知所有订阅者更新。 - 视图更新:
Watcher实例收到通知后,调用update方法更新对应的 DOM 元素。
vue3 双向数据绑定的原理
Vue3 使用 Proxy 对象来实现数据劫持,结合 ReactiveEffect 来管理依赖和更新,整体思路与 Vue2 类似,但更加高效灵活。
2.1 Proxy 数据劫持
Proxy 是 ES6 引入的新特性,它可以对整个对象进行代理,能够拦截对象的各种操作,包括属性的读取、赋值、删除等。
2.2 ReactiveEffect 管理依赖
Vue 3 引入了 ReactiveEffect 来管理依赖和更新。当一个 ReactiveEffect 运行时,会收集它所依赖的响应式数据,当这些数据发生变化时,会重新运行该 ReactiveEffect。
2.3. 整体流程
- 创建响应式对象:使用
reactive或ref函数创建响应式对象,内部使用Proxy进行数据劫持。 - 依赖收集:当
ReactiveEffect运行时,读取响应式对象的属性,触发Proxy的get拦截器,收集依赖。 - 数据更新:当响应式对象的属性发生变化时,触发
Proxy的set拦截器,通知所有依赖该属性的ReactiveEffect重新运行。 - 视图更新:
ReactiveEffect重新运行时,会更新对应的 DOM 元素。
综上所述,Vue2 和 Vue3 的双向数据绑定原理核心都是数据劫持和依赖管理,但实现方式上有所不同,Vue3 利用 Proxy 解决了 Vue 2 中 Object.defineProperty() 的一些局限性。