理解vue双向绑定原理

403 阅读3分钟

image.png Vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

流程:

  1. 在vue初始化时,对data数据进行劫持监听,也就是用监听器 Observe来监听所有属性。
  2. 若有属性发生变化时,通过Dep告诉订阅者Watcher看是否需要更新。(因为订阅者Watcher有多个,所以需要一个消息订阅器 Dep 来专门收集这些订阅者,在监听器Observe和订阅者Watcher之间进行统一管理。)
  3. 一个指令解析器 Compile ,对每个节点元素进行扫描解析,将相关的指令(如 v-model,v-on…)对应初始化一个订阅者Watcher(实例一个Watcher),并替换模板数据或者绑定相应函数
  4. 当订阅者Watcher接收到相应属性的变化通知,就会执行对应的更新函数,从而去更新视图

解释

  1. 一个监听器Observer,用来劫持并监听所有属性(vue2中用Object.defineProperty实现拦截监听;vue3中使用proxy),如果有变动的,就通知订阅者。(Observer负责将数据转为getter和setter形式)
  2. 一个订阅者Watcher,Watcher订阅者是Observer和Compile之间通信的桥梁,每个Watcher都绑定一个更新函数,Watcher可以收到属性的变化通知(收到dep.notice()通知)并执行相应的函数,从而更新视图(Watcher是实际上的数据依赖,负责将数据的变化转发到外界)
  3. 一个消息订阅器 Dep ,主要收集订阅者,当 Observe监听到发生变化,就通知Dep 再去通知Watcher去触发更新。(用于收集当前响应式对象的依赖关系,每个响应式对象包括子对象都拥有一个 Dep 实例(里面 subs 是 Watcher 实例数组),当数据有变更时,会通过 dep.notify()通知各个 watcher。)
  4. 一个解析器Compile,可以扫描和解析每个节点的相关指令(如 v-model,v-on …),若节点存在指令,则Compile初始化这类节点的模板数据(也就是将模版中的变量替换成数据,使其显示在视图上),以及初始化相应的订阅者(也就是将每个指令对应的节点绑定更新函数,添加监听数据的订阅者)

Watcher 和 Dep 的关系

watcher 中实例化了 dep 并向 dep.subs 中添加了订阅者,dep 通过 notify 遍历了 dep.subs 通知每个 watcher 更新。

总结

MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

首先将data传入Observer转成getter/setter形式;当Watcher实例读取数据时,会触发getter,被收集到Dep仓库中;当数据更新时,触发setter,通知Dep仓库中的所有Watcher实例更新,Watcher实例负责通知外界

juejin.cn/post/684490…
juejin.cn/post/711742…
juejin.cn/post/684490…