Vue源码阅读之Observer观察者模式(三)
在使用Vue开发过程中,数据双向绑定、响应式变化等特性极大降低了开发难度、提高了开发效率。但是我们对底层原理却知之甚少,知其然而不知其所以然。Vue响应式的核心是基于观察者模式实现的,今天我们就从源码上分析Vue内部实现的观察者模式。
一、概念
观察者模式是一种行为模式,定义了对象间一对多的关系。即被观察者发生变动时,会通知依赖他的所有观察者自动更新。观察者模式一般使用三个类,Vue中为Observer、Dep、Watcher。
- Observer类实现了观察初始逻辑,监听数据变动;
- Dep类上带有添加、解绑以及通知观察者的方法;
- Watcher类为具体观察者;
二、Vue中的观察者模式
下面我们以组件内部data数据为例,抽丝剥茧分析观察者模式是如何建立与运作的。
2.1 初始化ObServer、Dep
在组件内部状态state初始化完成后,会调用observer方法为data建立观察逻辑。
Vue实现观察者模式相关代码都在src/core/observer文件下。
在Observer/index.js文件中,会优先调用new Observer(value)进行初始化。
如果value是对象,Observer会为对象中的每组key/value值执行defineRective(),添加对象代理拦截。
defineReactive中会初始化dep实例,用于管理观察者在数据变动后作出响应。
注意到现在dep中订阅队列subs是空的,即还没有添加观察者。
2.2 将watcher添加到订阅队列中
之前只是初始化组件内部state数据,组件并没有挂载到页面上。挂载阶段才会将相应watcher添加到dep的订阅队列中。
在mountComponent挂载方法内部,会new Watcher()观察者实例,注意这个updateComponent方法很重要,会被添加到dep订阅队列中在数据变动时执行。
Watcher类中会调用get()方法,pushTarget、popTarget两个方法会为Dep类设置target静态属性,即此时Dep.target=当前watcher。
this.getter()方法即上一步传入的updateComponent(),执行后会调用render函数触发页面视图更新。
执行render函数后会获取state的值,这样就会执行observer中defineReactive的getter拦截中。
由于Dep.target是有值的,会通过dep,depend()方法为当前dep添加观察者。
当render函数执行完毕后,会调用popTarget重置Dep.target的值。到此Vue基于Observer、Dep、Watcher的观察者模式就建立起来了。
2.3 工作流程
观察者模式建立完成后,我们可以修改双向绑定的数据看看观察者模式是怎么工作的。
state数据变动会执行defineReactive中setter的拦截操作,通过dep.notify()通知所有观察者更新数据。
最后observer监听到被观察者变动会走到每个观察者实例中,执行run()方法中的get()。继而会调用当前watcher中的getter()方法,这里就是之前初始化时传入的updateComponent(),执行render函数重绘视图。
三、数据双向绑定实现原理
如果你对上述观察者模式的初始化及工作流程都了解清楚了,那么对于数据双向绑定的底层逻辑想必有了更加深入的认知。下面我们就一步步分析v-model双向绑定的原理。
- v-model是Vue默认指令,内部将表单常用事件--input、select等集成到指令中,但是最后都会进行赋值操作,即this.state=newValue,此时state的值发生改变但是视图并没有更新;
- 进行赋值后,就会被Observer监听到,进入到defineReactive的setter拦截中。继而dep.notify()发布订阅,通知观察者;
- watcher响应变化调用getter(),执行render函数更新视图。实现数据双向绑定逻辑。
四、自定义观察者模式
综上所述观察者模式的核心是三大类的实现,即Observer、Dep、Watcher。其核心是如何知晓被观察者数据的变动,ES5/ES6的Object.definePeoperty(obj, key, des)提供了很好的实现。Vue实现的观察者模式考虑了许多框架自身的东西--Vue实例初始化、VNode创建与更新等。我们可以实现一套简易的观察者模式。
const sharedPropertyDefinition = {enumerable: true,
configurable: true,
get: null,
set: null
}
// 初始化观察逻辑
function observer (target, sourceKey, key) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
subs.forEach(sub => sub.call(null, key, val))
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
// 订阅队列
const subs = new Set()
// 订阅队列添加观察者公共方法
function addSub (fn) {
subs.add(fn)
}
// 观察者
function watcher (key, val) {
console.log(`${key}最新值为:${val}`)
}
addSub(watcher)
效果如下:
系列相关文章:
Vue源码阅读之Vue构造函数(一)
Vue源码阅读之VNode虚拟DOM(二)
Vue源码阅读之Observer观察者模式(三)
以上是 Vue源码阅读之Observer观察者模式(三) 的全部内容, 来源链接: utcz.com/z/380867.html