响应式系统
每个组件实例都有相应的 Watcher
实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter
被调用时,会通知 Watcher
重新计算,从而使它关联的组件得以更新。
这里有三个重要的概念 Observe
、Dep
、Watcher
:
Observe
类主要给响应式对象的属性添加getter/setter
用于依赖收集与派发更新Dep
类用于收集当前响应式对象的依赖关系Watcher
类是观察者,实例分为渲染 watcher、计算属性 watcher、侦听器 watcher 三种
源码解析
initState
源码地址:src/core/instance/state.ts
的 initState
中:
export function initState(vm: Component) {
const opts = vm.$options
if (opts.props) initProps(vm, opts.props) // 初始化 props
// Composition API
initSetup(vm)
if (opts.methods) initMethods(vm, opts.methods) // 初始化 methods
if (opts.data) {
initData(vm) // 初始化 data
} else {
const ob = observe((vm._data = {}))
ob && ob.vmCount++
}
if (opts.computed) initComputed(vm, opts.computed) // 初始化 computed
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch) // 初始化 watch
}
}
Observer/defineReactive
源码地址:src/core/observer/index.ts
export function observe(
value: any,
shallow?: boolean,
ssrMockReactivity?: boolean
): Observer | void {
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (...) {
ob = new Observer(value, shallow, ssrMockReactivity)
}
return ob
}
这个方法主要用 data
作为参数去实例化一个 Observer
对象,Observer
是一个 Class,用于依赖收集和 notify
更新,Observer
的构造函数使用 defineReactive
方法给对象的键响应式化,给对象的属性递归添加 getter/setter
,当 data
被取值的时候触发 getter
并收集依赖,当被修改值的时候先触发 getter
再触发 setter
并派发更新
export class Observer {
dep: Dep
vmCount: number // number of vms that have this object as root $data
constructor(public value: any, public shallow = false, public mock = false) {
// this.value = value
this.dep = mock ? mockDep : new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (isArray(value)) {
// 数组的响应式处理
for (let i = 0, l = value.length; i < l; i++) {
observe(value[i], false, this.mock)
}
} else {
// 遍历对象的每一个属性并将它们转换为 getter/setter
const keys = Object.keys(value)
for (let i = 0; i < keys.length; i++) { // 把所有可遍历的对象响应式化
const key = keys[i]
defineReactive(value, key, NO_INIITIAL_VALUE, undefined, shallow, mock)
}
}
}
}
export function defineReactive(
obj: object,
key: string,
val?: any,
customSetter?: Function | null,
shallow?: boolean,
mock?: boolean
) {
const dep = new Dep() // 在每个响应式键值的闭包中定义一个 dep 对象
const property = Object.getOwnPropertyDescriptor(obj, key)
// 如果之前该对象已经预设了 getter/setter 则将其缓存,新定义的 getter/setter 中会将其执行
const getter = property && property.get
const setter = property && property.set
let childOb = !shallow && observe(val, false, mock)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val // 如果原本对象拥有 getter 方法则执行
if (Dep.target) { // 如果当前有 watcher 在读取当前值
dep.depend() // 那么进行依赖收集,dep.addSub
if (childOb) {
childOb.dep.depend()
if (isArray(value)) {
dependArray(value)
}
}
}
return isRef(value) && !shallow ? value.value : value
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val // 先执行 getter
if (!hasChanged(value, newVal)) { // 如果跟原来的值一样则不管
return
}
if (setter) {
setter.call(obj, newVal) // 如果原本对象拥有 setter 方法则执行
} else if (getter) {
return
} else if (!shallow && isRef(value) && !isRef(newVal)) {
value.value = newVal
return
} else {
val = newVal
}
dep.notify() // 如果繁盛变更,则通知更新,调用 watcher.update()
}
})
return dep
}
getter
的时候进行依赖的收集,注意这里,只有在 Dep.target
中有值的时候才会进行依赖收集,这个 Dep.target
是在 Watcher
实例的 get
方法调用的时候 pushTarget
会把当前取值的 watcher
推入 Dep.target
,原先的 watcher
压栈到 targetStack
栈中,当前取值的 watcher
取值结束后出栈并把原先的 watcher
值赋给 Dep.target
,cleanuoDeps
最后把新的 newDeps
里已经没有的 watcher
清空,以防止视图上已经不需要的无用 watcher
触发
setter
的时候首先执行 getter
,并且比对旧值是否变化,如果发生变更,则 dep
通知所有 subs
中存放的依赖数据的 Watcher
实例 update
进行更新,这里 update
中会 queueWatcher()
异步推送到调度者观察者队列 queue
中,在 nextTick
时 flushSchedulerQueue()
把队列中的 watcher
取出来执行 watcher.run
且执行相关钩子函数
Dep
上面多次提到一个关键词 Dep
,它是依赖收集的容器,或者称为依赖收集器,它记录了哪些 Watcher
依赖自己的变化,或者说哪些 Watcher
订阅了自己的变化
源码地址:src/core/observer/dep.ts
let uid = 0 // Dep 实例的id,为了方便去重
export default class Dep {
static target?: DepTarget | null // 当前是谁在进行依赖的收集
id: number
subs: Array<DepTarget> // 观察者集合
constructor() {
this.id = uid++ // Dep 实例的id
this.subs = [] // 存储收集器中需要通知的 Watcher
}
addSub(sub: DepTarget) {} // 添加一个观察者对象
removeSub(sub: DepTarget) {} // 移除一个观察者对象
depend(info?: DebuggerEventExtraInfo) {} // 依赖收集,当存在 Dep.target 时把自己添加到观察者的依赖中
notify(info?: DebuggerEventExtraInfo) {} // 通知所有订阅者
}
Dep.target = null
const targetStack: Array<DepTarget | null | undefined> = []
export function pushTarget(target?: DepTarget | null) { // 将 Watcher 观察者实例设置给 Dep.target,用于依赖收集,同时将该实例存入 target 栈中
targetStack.push(target)
Dep.target = target
}
export function popTarget() { // 将观察者实例从 target 栈中去除并设置给 Dep.target
targetStack.pop()
Dep.target = targetStack[targetStack.length - 1]
}
这里 Dep
的实例中的 subs
收集的依赖就是 watcher
,它是 Watcher
的实例,将来用来通知更新
Watcher
源码地址:src/core/observer/watcher.ts
export default class Watcher implements DepTarget {
...
constructor(
vm: Component | null,
expOrFn: string | (() => any),
cb: Function,
options?: WatcherOptions | null,
isRenderWatcher?: boolean // 是否时渲染 watcher 的标志位
) {
...
if (isFunction(expOrFn)) {
this.getter = expOrFn // 在 get 方法中执行
} else {
this.getter = parsePath(expOrFn)
}
this.value = this.lazy ? undefined : this.get() // lazy 主要针对计算属性,已办用于求值计算
}
// 获取 getter 的值并重新进行依赖收集
get() {
pushTarget(this)
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm)
} catch (e: any) {
...
} finally {
...
popTarget() // 将观察者实例从 target 栈中取出并设置给 Dep.target
this.cleanupDeps()
}
return value
}
addDep(dep: Dep) {} // 添加一个依赖关系到 Deps 集合中
cleanupDeps() {} // 清理 newDeps 里没有的无用 watcher 依赖
update() {} // 调度者接口,当依赖发生变化时进行回调
run() {} // 调度者工作接口,将被调度者回调
evaluate() {} // 收集该 watcher 的所有 deps 依赖
depend() {} // 收集该 watcher 的所有deps 依赖,只有计算属性使用
teardown() {} // 将自身从所有依赖收集订阅列表中删除
}
get
方法中执行的 getter
就是在一开始 new
渲染 watcher
时传入的 updateComponent = () => { vm._update(vm._render(), hydrating) }
,这个方法首先 vm._render()
生成渲染 VNode 树,在这个过程中完成对当前 Vue 实例 vm
上的数据访问,触发相应响应式对象的 getter
,然后 vm._update()
去 patch