细读Vue2.6.14 Core 源码(6): defineReactive
/**
@date 2021-09-12
@description 细读Vue2.6.14 Core 源码(6): defineReactive
*/
复制代码
壹(序)
这一章着重于 Vue 的响应式,通过调用 defineReactive 实现。
贰(defineReactive)
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
// 声明 dep,每个声明为响应式的值都有一个自己的 dep
const dep = new Dep()
// 获取属性描述符
const property = Object.getOwnPropertyDescriptor(obj, key)
// configurable 为 false 则 return
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
// 获取 getter 和 setter
const getter = property && property.get
const setter = property && property.set
// 此情况下赋值 val
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
// 观察者
// 对于非 object 的 value,只是简单的在 getter 中收集依赖
// 对于 object 会进行观察,产生一个观察者,在观察者中再调用 defineReactive 进行响应式处理,相当于递归处理
// 如果观察者中调用 defineReactive 时,又是一个 object,那么会再进行观察
// 此时就形成 defineReactive => observe => new Observer() => observer.walk() => defineReactive => observe...
// 通过此方式实现 object 的深层次响应式处理
// 对于数组则是进行判断,再遍历数组进行观察处理,又回到此处
let childOb = !shallow && observe(val)
// 使用 Object.defineProperty 设置 getter 和 setter
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// getter
get: function reactiveGetter () {
// 获取 value
const value = getter ? getter.call(obj) : val
// 在 pushTarget 函数中会赋值 Dep.target(初始值为 null,赋值为一个 watcher)
// 可以使用 popTarget 函数移除当前 Dep.target
// 保证只同时处理一个 watcher
// 在声明组件的 watcher 时会调用 watcher.get,将当前 watcher push进 targetStack,且 Dep.target = watcher
if (Dep.target) {
// 收集依赖
dep.depend()
if (childOb) {
// object 收集依赖
childOb.dep.depend()
if (Array.isArray(value)) {
// 数组收集依赖
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
// 获取当前 value
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
// 值并未变化,或都为 NaN
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
}
// 重新观察 newVal
childOb = !shallow && observe(newVal)
// 通知所有依赖更新
dep.notify()
}
})
}
复制代码
叁(observe)
export function observe (value: any, asRootData: ?boolean): Observer | void {
// 不是 object 或 是VNode则 return
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
// 已经被观察过了,直接使用 __b__
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve && // 需要被观察
!isServerRendering() && // 不是服务端渲染
(Array.isArray(value) || isPlainObject(value)) && // value 是 array 或 纯object
Object.isExtensible(value) && // 当前 value 是否可扩展
!value._isVue // 不是 Vue 实例
) {
// 创建新的观察者
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
复制代码
肆(Observer)
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
// 定义 __ob__
def(value, '__ob__', this)
// value 为数组
if (Array.isArray(value)) {
// 当前环境中对象有 __proto__ 属性
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
// 观察此数组,遍历调用 observe 进行递归
this.observeArray(value)
} else {
// 调用 walk 函数
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])
}
}
}
复制代码
伍(Dep)
export default 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()
}
}
}
复制代码
终(导航)
细读Vue2.6.14 Core 源码(2): After Vue
细读Vue2.6.14 Core 源码(3): initGlobalAPI
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END