data数据绑定-观察者模式
在new Vue()时,会触发vue中的_init方法,然后在initState中操作data
initState
export function initState(vm: Component) {
//初始化watchers观察者数组
vm._watchers = []
........
if (opts.data) {
initData(vm)
}
.......
}
调用initData开始初始化data对象
initData
function initData(vm: Component) {
//获取data
let data = vm.$options.data
//判断data是函数还是对象
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// 获取data的key
const keys = Object.keys(data)
....和prop,methods进行name查重操作
// observe data
observe(data, true /* asRootData */)
}
observe
export function observe(value: any, asRootData: ?boolean): Observer | void {
//判断是否为对象
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
//创建观察者对象
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
Observer
constructor(value: any) {
//如果是数组会有去重写数组的原型,我这边主要看walk方法
this.walk(value)
}
walk(obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
//这里最重要的就是重写get和set方法
defineReactive(obj, keys[i])
}
}
defineReactive
const dep = new Dep()
Object.defineProperty(obj, key, {
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val //如果有属于自己的get方法,调用自己的get方法
if (Dep.target) { //Dep.target就是每一个vue文件或者组件对应的Watcher对象
dep.depend() //将创建的dep添加到Watcher中
}
return value
},
set: function reactiveSetter(newVal) {
.....
dep.notify() //当值发生改变时,通知更新dom
}
})
depend->addDep->addSub
首先看depend,Dep.target就是组件的Watcher对象
Dep
depend() {
if (Dep.target) {
Dep.target.addDep(this)
}
}
我们再看Watcher中的addDep方法
Watcher
addDep(dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep) //将dep添加到Watcher的newDeps中
if (!this.depIds.has(id)) {
dep.addSub(this) //同时也将Watcher添加到dep的subs中
}
}
}
Dep
addSub(sub: Watcher) {
this.subs.push(sub)
}
现在就是你中有我,我中有你了
然后来看Watcher是怎么生成的,同时值的get是什么时候触发的
mounted
我们可以在mount时,找到一个方法mountComponent
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
mountComponent
我们留下一些关键代码。可以看到new Watcher,同时将vm._update作为回调函数传给了Watcher,
vm._update(vm._render(), hydrating)可以理解为更新dom的方法,同时vm._render()在生成虚拟dom时,会触发data中value的get方法
vm._update执行完便走到了vue生命周期的mounted
vm.$el = el
let updateComponent
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
before() {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true )
hydrating = false
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
Watcher
接下来看watcher
Watcher
constructor(
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {
vm._watcher = this
}
vm._watchers.push(this)
this.cb = cb
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = process.env.NODE_ENV !== 'production'
? expOrFn.toString()
: ''
if (typeof expOrFn === 'function') {
this.getter = expOrFn //就是update方法
}
this.value = this.lazy
? undefined
: this.get()
}
在watcher的构造函数中调用了get方法
export function pushTarget(target: ?Watcher) {
targetStack.push(target)
Dep.target = target
}
export function popTarget() {
targetStack.pop()
Dep.target = targetStack[targetStack.length - 1]
}
get() {
//让Dep.target指向watcher
pushTarget(this);
let value
const vm = this.vm
try {
//调用update方法更新dom元素,同时触发defineProperty中的get方法,将dep和watcher关联起来
value = this.getter.call(vm, vm)
} catch (e) {
} finally {
if (this.deep) {
traverse(value)
}
//释放Dep.target
popTarget()
this.cleanupDeps() //更新deps
}
return value
}
到上面为止数据响应中的get已经看完了,接下来看set,也就是数据改变时
set
先回到defineReactive中,找到set方法
set: function reactiveSetter(newVal) {
.......
dep.notify()
}
然后再找到notify,通知更新
notify() {
const subs = this.subs.slice();
if (process.env.NODE_ENV !== 'production' && !config.async) {
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
//subs[i]就是Watcher对象
subs[i].update()
}
}
然后找到Watcher中的update方法
watcher.update -> queueWatcher -> flushSchedulerQueue -> watcher.run
按照上面的执行顺序,直接看run就行了,中间做了什么操作可以自己去研究研究
update() {
......
queueWatcher(this)
}
export function queueWatcher(watcher: Watcher) {
.....
nextTick(flushSchedulerQueue)
}
function flushSchedulerQueue() {
.....
for (index = 0; index < queue.length; index++) {
watcher = queue[index]
if (watcher.before) {
watcher.before()
}
watcher.run()
}
}
run
run() {
if (this.active) {
const value = this.get() //调用vm._update重新渲染
}
}