上篇文章【源码解析】建立Vue实例时干了什么?提到响应数据这个词,到底什么样的数据是响应数据呢?node
小提示:配合源码食用更佳美味。react
先讲这样一个过程。数组
在$mont()的时候,会建立Watcher实例的过程,把Dep.target设置为当前Watcher,而后会开始render,render的时候就会读取到响应数据,从而触发get,只有被观察的数据才配置了get,get执行过程当中会建立一个Dep实例,此时有了Watcher和Dep,他们会创建关系。他们创建关系以后,当一旦被观察的数据发生改变,就会触发set,set调用dep.notify(),dep则会让跟他有关系的Watcher进行更新。缓存
被观察的数据更改会致使组件进行更新从而影响到dom的改变。这个被观察的数据就是响应数据,而这个get的过程咱们叫作依赖收集(后续分析)。性能优化
在init过程当中 咱们调用了initState -> initData闭包
src/core/instance/state.jsapp
// 观察data
observe(data, true /* asRootData */)
复制代码
看一下observe
函数的实现。进行各类判断,最终返回Observer实例。dom
export function observe (value: any, asRootData: ?boolean): Observer | void {
// vnode和不是对象的不须要被观察
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
// 性能优化:带有__ob__的是已经被观察的数据 直接返回value.__ob__
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
) {
// 须要被观察的 直接建立Observer实例
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
复制代码
因为数组和普通对象的劫持方式不一样,所以 新建一个Observer类进行来统一。函数
export class Observer {
value: any;
dep: Dep;
vmCount: number;
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
// __ob__赋值this,性能优化并表示数据已经被观察
def(value, '__ob__', this)
if (Array.isArray(value)) {
// 数组的观察逻辑
// 重写数组方法进行响应 见下文
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
// 观察数组
this.observeArray(value)
} else {
// 对象的观察逻辑
this.walk(value)
}
}
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
// 定义响应数据 obj
defineReactive(obj, keys[i])
}
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
// 观察每一项
observe(items[i])
}
}
}
复制代码
对数组方法进行重写, 而且响应数据源码分析
src/core/observer/array.js
import { def } from '../util/index'
// 数组原型
const arrayProto = Array.prototype
// 建立新对象 不会对原数组产生影响
export const arrayMethods = Object.create(arrayProto)
// 常会对着7个方法进行劫持,仅有这7个方法会对数组改变
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method) {
// 缓存原来的方法
const original = arrayProto[method]
// 重写方法
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)
// 触发更新 重中之重就是这个地方 依赖收集的时候分析
ob.dep.notify()
return result
})
})
复制代码
调用了defineReactive(obj, keys[i])
export function defineReactive ( obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean ) {
const dep = new Dep()
// 这个对象可能设置过getter和setter,须要多调用一次用户配置的
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
// 孩子是对象的递归观察
let childOb = !shallow && observe(val)
// 劫持get和set 响应数据核心
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// 先掉一下用户的getter
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
// 同样的值则不须要被赋值
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (getter && !setter) return
if (setter) {
// 用户的setter
setter.call(obj, newVal)
} else {
// 赋值 闭包
val = newVal
}
// 被设置的须要进行观察
childOb = !shallow && observe(newVal)
// 触发更新
dep.notify()
}
})
}
复制代码
感谢各位的阅读,错误是在所不免的,如有错误,或者有更好的理解,请在评论区留言,再次感谢。但愿你们相互学习,共同进步。