今日目标:找到响应式文件(憋一发大的)
路径:src\core\observer\index.js
在之前提到过流程中,出现过许多次“将其添加到观察者中”、“将其响应化”、“通过defineReactive将其添加到观察者中”诸如此类的描述
这些其实就是对vue响应式的描述,提到过这么久的响应式,今天将带领大家进入这神奇的世界,看看他的入口
入口
src\core\instance\state.js在initData中,可以看到最后通过Observe将data变成响应化
observe(data, true /* asRootData */)
可以通过ctrl+左键跟踪到observe所在的文件
现在来到了src\core\observer\index.js
可以看到已经出了之前的instance文件夹,进入了observer文件夹中,此文件夹中的文件就是vue响应式实现的所有代码了。今天就引出这个文件夹,因为分开讲响应式的话会很乱很懵,因此下几期将带来observer文件夹和响应式的分析!
这里先附上src\core\observer\index.js这个文件的逐行分析,可以先看看
/* @flow */
import Dep from './dep'
import VNode from '../vdom/vnode'
import { arrayMethods } from './array'
import {
def,
warn,
hasOwn,
hasProto,
isObject,
isPlainObject,
isPrimitive,
isUndef,
isValidArrayIndex,
isServerRendering
} from '../util/index'
// 方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作
// 为名称的属性)组成的数组,只包括实例化的属性和方法,不包括原型上的。
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
/**
* In some cases we may want to disable observation inside a component's
* update computation.
* 在某些情况下,我们可能希望禁用组件内部的观察更新计算。
*/
export let shouldObserve: boolean = true
// 是否可以添加到观察者模式
export function toggleObserving (value: boolean) {
shouldObserve = value
}
/**
* Observer class that is attached to each observed
* object. Once attached, the observer converts the target
* object's property keys into getter/setters that
* collect dependencies and dispatch updates.
* 附加到每个被观察者的观察者类对象。一旦链接,观察者就会转换目标对象的属性键放入getter/setters中收集依赖项并发送更新。
*/
// Observer类 :todo
export class Observer {
value: any;
// Dep类
dep: Dep;
vmCount: number; // number of vms that have this object as root $data 将此对象作为根$数据的VM数
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
// def添加__ob__属性,value必须是对象
def(value, '__ob__', this)
// 判断当前value是不是数组
if (Array.isArray(value)) {
// 如果是数组
// 检测当前浏览器中有没有Array.prototype
// 当能使用__proto__时
// 这里完成了数组的响应式,不使用这7个方法都不会触发响应式
if (hasProto) {
// 有原型时 将arrayMethods覆盖value.__proto__,也就是把增加了副作用的7个数组方法放了进来
protoAugment(value, arrayMethods)
} else {
// 复制增加了副作用的7个数组方法
copyAugment(value, arrayMethods, arrayKeys)
}
// 遍历将数组所有元素进行observe
this.observeArray(value)
} else {
// 不是数组是对象,执行这里
// walk就是给对象的所有key进行响应化
this.walk(value)
}
}
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
* 遍历所有属性,将其转换为getter/setters。这个方法只应该在value的类型为对象时调用
*/
// walk就是给对象的所有key进行响应化
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
// 遍历对象的每个key,通过defineReactive进行响应化
defineReactive(obj, keys[i])
}
}
/**
* Observe a list of Array items.
*/
// 遍历将数组所有元素进行observe
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
// helpers
/**
* Augment a target Object or Array by intercepting
* the prototype chain using __proto__
* 通过拦截来扩充目标对象或数组原型链使用__proto__
*/
function protoAugment (target, src: Object) {
/* eslint-disable no-proto */
// 这里直接用劫持的7个数组覆盖
target.__proto__ = src
/* eslint-enable no-proto */
}
/**
* Augment a target Object or Array by defining
* hidden properties.
* 通过定义隐藏属性。
*/
/* istanbul ignore next */
// target: value数组 src arrayMethods keys arrayKeys
function copyAugment (target: Object, src: Object, keys: Array<string>) {
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
// 给target设置key属性 内容为src[key] 也就是arrayMethods的值
def(target, key, src[key])
}
}
/**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
* 尝试给一个value对象创建一个observer实例,
* 如果观察成功,返回一个新的observer实例
* 或者返回一个已经存在的observer 如果这个value对象早已拥有
*/
// observe作用就是为了拿到Observe实例并返回,从缓存中或者new一个
export function observe (value: any, asRootData: ?boolean): Observer | void {
// 判断是否为对象 判断是否为VNode
if (!isObject(value) || value instanceof VNode) {
// 如果不是对象 或者 是实例化的Vnode 也就是vdom
return
}
// 观察者 创建一个ob
let ob: Observer | void
// 检测是否有缓存ob
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
// 直接将缓存的ob拿到
ob = value.__ob__
} else if (
// 如果没有缓存的ob
shouldObserve && // 当前状态是否能添加观察者
!isServerRendering() && // 不是ssr
(Array.isArray(value) || isPlainObject(value)) && // 是对象或数组
Object.isExtensible(value) && // 是否可以在它上面添加新的属性
!value._isVue // 是否是Vue实例
) {
// new 一个Observer实例 复制给ob
ob = new Observer(value)
}
// 如果作为根data 并且当前ob已有值
if (asRootData && ob) {
// ++
ob.vmCount++
}
// 最后返回ob,也就是一个Obesrver实例 有这个实例就有__ob__,然后其对象和数组都进行了响应化
return ob
}
/**
* Define a reactive property on an Object.
* 在对象上定义一个响应式属性
*/
export function defineReactive (
obj: Object, // 对象
key: string, // 对象的key
val: any, // 监听的数据
customSetter?: ?Function, //日志函数
shallow?: boolean // 是否要添加__ob__属性
) {
// 实例化一个Dep对象, 其中有空的观察者列表
const dep = new Dep()
// 获取obj的key的描述符
const property = Object.getOwnPropertyDescriptor(obj, key)
// 检测key中是否有描述符 如果是不可配置 直接返回
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
// 满足预定义的getter/setters
// 获取key中的get
const getter = property && property.get
// 获取key中的set
const setter = property && property.set
// 如果getter不存在或setter存在 并且参数长度为2
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
// 递归响应式处理 给每一层属性附加一个Obeserver实例
// shallow不存在时代表没有__ob__属性 将val进行observe返回一个ob实例赋值给childOb
let childOb = !shallow && observe(val)
// 数据拦截
// 通过Object.defineProperty对obj的key进行数据拦截
Object.defineProperty(obj, key, {
// 枚举描述符
enumerable: true,
// 描述符
configurable: true,
get: function reactiveGetter () {
// 如果有用户定义的getter,就用用户定义的getter
const value = getter ? getter.call(obj) : val
// 判断是否有Dep.target 如果有就代表Dep添加了Watcher实例化对象
if (Dep.target) {
// 加入到dep去管理watcher
dep.depend()
// 如果存在子对象
if (childOb) {
// 也加进去管理
childOb.dep.depend()
// 如果值是数组,要特殊处理
if (Array.isArray(value)) {
// 循环添加watcher
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
// 获取value值 触发依赖收集
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
// 对于没有setter的访问器属性 返回
if (getter && !setter) return
// 如果setter存在
if (setter) {
// 设置新值
setter.call(obj, newVal)
} else {
// 如果没有setter ,直接给新值
val = newVal
}
// 递归,对新来的值 对新值进行observe 返回ob实例
childOb = !shallow && observe(newVal)
// 当set时触发通知
dep.notify()
}
})
}
/**
* Set a property on an object. Adds the new property and
* triggers change notification if the property doesn't
* already exist.
* 给对象设置一个属性,添加新属性和添加触发更改通知(dep.notify),如果这个属性不是早已存在
* Vue.set
*/
export function set (target: Array<any> | Object, key: any, val: any): any {
if (process.env.NODE_ENV !== 'production' &&
// 判断数据 是否是undefined或者null
// 判断数据类型是否是string,number,symbol,boolean
(isUndef(target) || isPrimitive(target))
) {
// target必须是对象或者数组,否则发出警告
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// 如果是数组 并且检查key是否是有效的数组索引
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 设置数组长度
target.length = Math.max(target.length, key)
// 像数组尾部添加一个新数据,相当于push
target.splice(key, 1, val)
// 返回val
return val
}
// 如果key在target上 并且不是通过原型链查找的
if (key in target && !(key in Object.prototype)) {
// 赋值
target[key] = val
return val
}
// 声明一个对象ob 值为该target对象中的原型上面的所有方法和属性,表明该数据加入过观察者中
const ob = (target: any).__ob__
// 如果是vue 或者 检测vue被实例化的次数 vmCount
if (target._isVue || (ob && ob.vmCount)) {
// 如果不是生产环境,发出警告
// 避免添加响应式属性给vue实例或者根$data
process.env.NODE_ENV !== 'production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
// 如果ob不存在,证明没有添加观察者,不是相应,直接赋值返回
if (!ob) {
target[key] = val
return val
}
// 通过defineReactive将ob.value加入的观察者
defineReactive(ob.value, key, val)
// 触发通知更新,通知订阅者obj.value更新数据
ob.dep.notify()
return val
}
/**
* Delete a property and trigger change if necessary.
* 删除属性并在必要时触发更改数据。
* Vue.delete
*/
export function del (target: Array<any> | Object, key: any) {
// 如果不是生产环境
if (process.env.NODE_ENV !== 'production' &&
// 是否是undefined null sttring boolean symbol number
(isUndef(target) || isPrimitive(target))
) {
// 发出警告,无法删除这些值
warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// 如果是数组 并且检查key是否是有效的数组索引
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 使用splice删除
target.splice(key, 1)
// 返回
return
}
// 获取__ob__属性
const ob = (target: any).__ob__
// 如果是vue 或者vue的实例化次数不为0
if (target._isVue || (ob && ob.vmCount)) {
// 如果生产环境 发出警告 不能删除vue实例上的响应式属性
process.env.NODE_ENV !== 'production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
// 返回
return
}
// 如果不是target对象本身的属性,因为delete只能删除自身对象的属性
if (!hasOwn(target, key)) {
// 返回
return
}
// 删除对象中的属性方法
delete target[key]
// 如果没有__ob__属性,代表没有添加观察者
if (!ob) {
// 直接返回
return
}
// 通知更新 更新数据
ob.dep.notify()
}
/**
* Collect dependencies on array elements when the array is touched, since
* we cannot intercept array element access like property getters.
* 在接触数组时收集对数组元素的依赖关系,因为我们不能像属性getter那样拦截数组元素访问。
*/
function dependArray (value: Array<any>) {
for (let e, i = 0, l = value.length; i < l; i++) {
e = value[i]
// 判断是否存在__ob__实例,并且每个都调用depend添加wathcer管理
e && e.__ob__ && e.__ob__.dep.depend()
// 递归完数组所有内容,直到不是数组,跳出递归
if (Array.isArray(e)) {
dependArray(e)
}
}
}