赞
踩
在数据变化的时候,视图也会随着改变,这就是响应式
VUE使用Object.defineProperty 中getter和setter方法中的观察者模式来实现响应式
这个方法是对象方法,在一个对象上定义一些新的属性及方法,或改变对象的现有方法,并返回这个对象。
举个示例看一下:
var mVal = 0; var o = {}; Object.defineProperty(o, 'm'){ get: function(){ console.log('这里监听获取m值"); return mVal; }, set: function(newVal){ console.log('这里监听修改m值"); mVal = newVal; }, enumerable : true, configurable : true } o.m = 88; console.log(o.m); // 88
分析:当调用o.m给对象o中的m属性赋值的时候,会调用set方法,将m的值赋给mVal,此时就会调用get方法,获取这个值。
通过这种方式我们就可以实现一个简单的vue双向绑定了,给data中的所有属性加上get和set方法。
观察者模式分为注册环节与发布环节。
将需要修改的属性集中注册一下,当处理完后一起发布出去。
function Observer(){ this.dep = []; register(fn){ this.dep.push(fn) } notify(){ this.dep.forEach(item => item()) } } 依次注册多个想要执行的函数 const wantCake = new Observer(); wantCake.register(console.log('call dish')); wantCake.register(console.log('call jaks')); wantCake.register(console.log('call mejdh')); 在完成后通知所有客户并执行函数 wantCake.notify()
首先参考一张原理图
我们分3个步骤来解释
1、init阶段,VUE实例的data属性reactive化,加上get、set方法
function defineReactive(obj: Object, key: String, ...){ const dep = new Dep(); Object.defineProperty(o, key){ get: function reactiveGetter(){ ... dep.depend(); return value; ... }, set: function reactiveSetter(newVal){ ... val = newVal; dep.notify(); ... }, enumerable : true, configurable : true } } const Dep{ static target: ?Watcher; subs: Array<Watcher>; depend () { if (Dep.target) { Dep.target.addDep(this) } } notify () { const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } }
这里的dep是一个观察者类,每一个属性都有一个dep,调用getter时去dep里注册函数,在调用setter时,执行注册的函数。
2、mount阶段
mountComponent(vm: Component, el: ?Element, ...) { vm.$el = el ... updateComponent = () => { vm._update(vm._render(), ...) } new Watcher(vm, updateComponent, ...) ... } class Watcher { getter: Function; // 代码经过简化 constructor(vm: Component, expOrFn: string | Function, ...) { ... this.getter = expOrFn Dep.target = this // 注意这里将当前的Watcher赋值给了Dep.target this.value = this.getter.call(vm, vm) // 调用组件的更新函数 ... } }
在mount时会创建一个Watcher类,这个类是链接dep与vue组件的桥梁。每一个watcher对应一个vue component。
在new Watcher()时会调用组件的getter方法,此时会调用render重新渲染函数。
render函数会访问data属性,这时就去调用这个属性的getter函数
// getter函数
get: function reactiveGetter () {
....
dep.depend()
return value
....
},
// dep的depend函数
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
在depend函数中,dep就是watcher对象本身。这样每次渲染这个组件时,如果用到了这个属性,组件对应的watcher都会注册到这个属性的dep中。这个过程被称为依赖收集。
在收集完所有的依赖后,如果这个属性变化,就会通知watcher去更新相关的组件。
3、更新阶段
属性改变时,回去调用dep里面的notify函数,然后通知所有的watcher去调用update函数进行更新。
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
4、流程展示
reactive属性
setter
Dep
notify
watcher
re-render
VueComponent
5、总结一下
第一步:组件初始化时给data中所有属性添加get、set,reactive化;然后注册一个Watcher对象,此时watcher会立即调用组件里的render去生成虚拟DOM,此时会用到data,所以会触发getter函数,将当前的watcher注册到sub里。
第二步:在data属性变化时,会遍历sub中所有watcher对象,通知它们去渲染组件。
借鉴文章:https://zhuanlan.zhihu.com/p/88648401
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。