vue2的响应式主要是数据变为响应式、依赖收集、派发更新三大步骤
Observer类
Observe的主要作用是通过Object.defineProperty方法把数据变为响应式
class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
constructor (value: any) {
this.value = value
// Dep是存储依赖的容器
this.dep = new Dep()
this.vmCount = 0
// 每一个响应式的数据都有一个__ob__属性,挂载的是Observer实例
def(value, '__ob__', this)
// 如果数据是数组,那么要把数组的七个方法变为处理过的可以响应式的方法
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
// 对数组的每一个元素进行响应式处理,这里是就是调用了observe函数,observe函数主要内容就是new Observe(value)
this.observeArray(value)
} else {
// 如果是对象,对对象的每一个成员执行defineReactive函数,这个函数核心就是Object.defineProperty
this.walk(value)
}
}
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
/**
* Observe a list of Array items.
*/
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
数组的响应式
下面是对数组的七个方法响应式处理的核心方法
/*
* not type checking this file because flow doesn't play well with
* dynamically accessing methods on Array prototype
*/
import { def } from '../util/index'
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method]
// def函数就是直接调用了Object.defineProperty,
//把methodsToPathch对象(一个数组)的七个成员的值挂载为一个函数,调用这些方法的时候会触发这个函数
def(arrayMethods, method, function mutator (...args) {
// 在挂载上去的函数里面,显示调用了自带的方法,然后把得到的值返回,
// 但是中间做了响应式的处理,同时通知所有订阅者更新
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
// 下面几个方法会向数组插入新的元素,新的元素也要过一下响应式处理
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
// 这里通知订阅者更新
ob.dep.notify()
return result
})
})
Watcher类
收集依赖之后, Watcher保存在Deps中,数据发生变动时,由Deps通知Watcher,由Watcher通知cb进行视图更新
// 代码过长,省略一些, 主要看他的几个方法
export default class Watcher {
...
/**
* Evaluate the getter, and re-collect dependencies.
*/
get () {
// 是Dep.target进行依赖收集
pushTarget(this)
let value
const vm = this.vm
try {
// 触发getter
value = this.getter.call(vm, vm)
} catch (e) {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
// 触发依赖的每个成员的getter,收集依赖,相关方法在defineReactive中定义
traverse(value)
}
popTarget()
this.cleanupDeps()
}
return value
}
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
// 异步推送到观察者队列中,由调度着调用
queueWatcher(this)
}
}
/**
* Scheduler job interface.
* Will be called by the scheduler.
*/
// 触发更新
run () {
if (this.active) {
const value = this.get()
if (
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
// set new value
const oldValue = this.value
this.value = value
if (this.user) {
try {
this.cb.call(this.vm, value, oldValue)
} catch (e) {
handleError(e, this.vm, `callback for watcher "${this.expression}"`)
}
} else {
this.cb.call(this.vm, value, oldValue)
}
}
}
}
}
Dep类
主要用来收集依赖,和派发更新
class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
if (process.env.NODE_ENV !== 'production' && !config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
defineReactive
通过Object.defineProperty为对象添加getter和setter,依赖收集时,Watcher实例存在Deps中,setter时,通知所有的Watcher进行更新
function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
// 保存原来的getter和setter,在新的getter和setter中执行
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
//派发更新
dep.notify()
}
})
}