准备花4天将这些整理完毕,分享给大家,因此下一期将在4天后!这里先把所有文件的注释奉上
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中收集依赖项并发送更新。
*/
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
// 这里会new一个Dep实例
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
// 也是把value进行响应化,并返回一个ob实例,还添加了__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
// 如果是对象继续调用 observe(val) 函数观测该对象从而深度观测数据对象
// walk 函数中调用 defineReactive 函数时没有传递 shallow 参数,所以该参数是 undefined
// 默认就是深度观测
let childOb = !shallow && observe(val)
// 数据拦截
// 通过Object.defineProperty对obj的key进行数据拦截
Object.defineProperty(obj, key, {
// 枚举描述符
enumerable: true,
// 描述符
configurable: true,
get: function reactiveGetter () {
// 获取值
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)
}
}
}
dep.js
/* @flow */
import type Watcher from './watcher'
import { remove } from '../util/index'
import config from '../config'
let uid = 0
/**
* A dep is an observable that can have multiple
* directives subscribing to it.
* dep是一个可观测到的,可以有多个订阅它的指令
*/
export default class Dep {
// 一个静态属性 target,这是一个全局唯一 Watcher
// 同一时间只能有一个全局的 Watcher 被计算
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
// uid
this.id = uid++
// 存放Watcher对象的数组
this.subs = []
}
addSub (sub: Watcher) {
// 给subs数组添加一个Watcher对象
this.subs.push(sub)
}
removeSub (sub: Watcher) {
// 删除watcher对象
remove(this.subs, sub)
}
// 添加watcher
// 为Watcher.newDeps.push(dep) 一个dep对象
depend () {
// target就是Watcher dep就是dep对象,dep中是否有watcher对象
if (Dep.target) {
// 用当前的watcher调用addDep
// :todo 为了多对多关系,得分析addDep
Dep.target.addDep(this)
}
}
// 通知所有watcher对象更新视图,也就是执行update
notify () {
// stabilize the subscriber list first
// 浅拷贝一份subs数组,也就是Watchers列表
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
// 如果不运行async,则不会在调度程序中对sub进行排序
// 我们现在需要对它们进行分类以确保它们发射正确秩序
subs.sort((a, b) => a.id - b.id)
}
// 所有subs中的wathcers执行update函数,也就是更新
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
// The current target watcher being evaluated.
// This is globally unique because only one watcher
// can be evaluated at a time.
// 正在评估的当前目标观察程序。
// 这是全局的的,因为只有一个观察者,可以在任何时候都被评估。
Dep.target = null
const targetStack = []
// 压栈
export function pushTarget (target: ?Watcher) {
// 压栈
targetStack.push(target)
// target就是watcher dep是Dep对象
Dep.target = target
}
export function popTarget () {
// 出栈
targetStack.pop()
// 成为最后一个元素
Dep.target = targetStack[targetStack.length - 1]
}
watcher.js
/* @flow */
import {
warn,
remove,
isObject,
parsePath,
_Set as Set,
handleError,
noop
} from '../util/index'
import { traverse } from './traverse'
import { queueWatcher } from './scheduler'
import Dep, { pushTarget, popTarget } from './dep'
import type { SimpleSet } from '../util/index'
let uid = 0
/**
* A watcher parses an expression, collects dependencies,
* and fires callback when the expression value changes.
* This is used for both the $watch() api and directives.
* 观察者分析表达式,收集依赖关系,
* 并在表达式值更改时触发回调。
* 它同时用于$watch()api和指令。
*/
export default class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deep: boolean;
user: boolean;
lazy: boolean;
sync: boolean;
dirty: boolean;
active: boolean;
deps: Array<Dep>;
newDeps: Array<Dep>;
depIds: SimpleSet;
newDepIds: SimpleSet;
before: ?Function;
getter: Function;
value: any;
constructor (
vm: Component, // dom
expOrFn: string | Function, //获取值的函数,或是更新视图的函数
cb: Function, //回调函数
options?: ?Object, //参数
isRenderWatcher?: boolean //是否是渲染过的watcher
) {
// 获取到vm
this.vm = vm
// 如果是已经渲染过的watcher
if (isRenderWatcher) {
// 把当前Watcher对象给_wathcer
vm._watcher = this
}
// 把观察者添加到队列里面 当前Watcher添加到vue实例上
vm._watchers.push(this)
// options
// 如果有参数
if (options) {
// 获取参数
this.deep = !!options.deep
this.user = !!options.user
this.lazy = !!options.lazy
this.sync = !!options.sync
this.before = options.before
} else {
// 否则都为false
this.deep = this.user = this.lazy = this.sync = false
}
this.cb = cb // 回调函数
this.id = ++uid // uid for batching uid用于批处理
this.active = true // 激活
this.dirty = this.lazy // for lazy watchers 用于懒惰的观察者
this.deps = [] // 观察者队列
this.newDeps = [] // 新的观察者队列
this.depIds = new Set() // depId 不可重复
this.newDepIds = new Set() // 新depId 不可重复
this.expression = process.env.NODE_ENV !== 'production'
? expOrFn.toString() // 函数变成字符串形式
: ''
// parse expression for getter
// getter的解析表达式
// 如果是函数
if (typeof expOrFn === 'function') {
// 获取值函数
this.getter = expOrFn
} else {
// 如果是keepAlive 组件则走这里
// 匹配不是 数字字母下划线 $符号 开头的为true
this.getter = parsePath(expOrFn)
if (!this.getter) {
// 如果不存在
// 给一个noop空函数
this.getter = noop
process.env.NODE_ENV !== 'production' && warn(
`Failed watching path: "${expOrFn}" ` +
'Watcher only accepts simple dot-delimited paths. ' +
'For full control, use a function instead.',
vm
)
}
}
//
this.value = this.lazy
? undefined // 当lazy为真时
: this.get() // lazy不在时 计算getter,并重新收集依赖项。
}
/**
* Evaluate the getter, and re-collect dependencies.
* 计算getter,并重新收集依赖项。
*/
get () {
// 添加dep.target dep.target = this
pushTarget(this)
let value
const vm = this.vm
try {
// 这时dep.target = this ,然后执行this.getter.call也就触发get方法,判断dep.target是否存在,存在则dep.depend()
// 获取值 触发get 也就触发Object.definePorperty的get中的dep.depend(),依赖收集
// 每个watcher第一次实例化的时候,都会作为订阅者订阅其相应的Dep。
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) {
// 为 seenObjects 深度收集val 中的key
traverse(value)
}
// 出栈一个dep.target
popTarget()
// 清理依赖项集合
this.cleanupDeps()
}
// 返回值
return value
}
/**
* Add a dependency to this directive.
* 向该指令添加依赖项
*/
addDep (dep: Dep) {
// dep.id 陆续自+
const id = dep.id
// 如果id不存在
if (!this.newDepIds.has(id)) {
// :todo
// 你保存我的引用
// 我也要保存你的引用
// newDepIds添加一个id
this.newDepIds.add(id)
// newDeps添加一个dep
this.newDeps.push(dep)
// 如果depIds中id不存在
if (!this.depIds.has(id)) {
// 给subs数组添加一个Watcher对象
dep.addSub(this)
}
}
}
/**
* Clean up for dependency collection.
* 清理依赖项集合。
*/
cleanupDeps () {
// 获取deps长度
let i = this.deps.length
// 遍历
while (i--) {
const dep = this.deps[i]
// 如果在newDepIds中不存在dep的id
if (!this.newDepIds.has(dep.id)) {
// 清楚依赖项
dep.removeSub(this)
}
}
let tmp = this.depIds //获取depid
this.depIds = this.newDepIds // 获取新depids
this.newDepIds = tmp // 旧覆盖新
this.newDepIds.clear() // 清空对象
// 互换
tmp = this.deps
this.deps = this.newDeps
this.newDeps = tmp
this.newDeps.length = 0
}
/**
* Subscriber interface.
* Will be called when a dependency changes.
* 观察者接口
* 将在依赖项更改时触发
*/
update () {
/* istanbul ignore else */
// 如果是懒惰的lazy
if (this.lazy) {
//
this.dirty = true
} else if (this.sync) { //如果是同步
//
this.run()
} else {
// :todo 异步队列
// 数据并不会立即更新,而是异步,批量排队执行
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) ||
// 获取deep 如果为true
this.deep
) {
// set new value
// 设置新值
const oldValue = this.value
// 赋值新值
this.value = value
// 如果是user
if (this.user) {
try {
// 更新回调函数
this.cb.call(this.vm, value, oldValue)
} catch (e) {
// 如果出错
handleError(e, this.vm, `callback for watcher "${this.expression}"`)
}
} else {
// 如果不是user,更新回调函数 获取到新的值 和旧的值
this.cb.call(this.vm, value, oldValue)
}
}
}
}
/**
* Evaluate the value of the watcher.
* This only gets called for lazy watchers.
* 评估观察者的价值。这只适用于懒惰的观察者
*/
evaluate () {
// 获取值
this.value = this.get()
// 懒惰lazy标志,标志已经获取过一次值
this.dirty = false
}
/**
* Depend on all deps collected by this watcher.
* 依赖于此观察者收集的所有DEP。
* 循环deps 收集 newDeps dep 当newDeps 数据被清空的时候重新收集依赖
*/
depend () {
let i = this.deps.length
while (i--) {
this.deps[i].depend()
}
}
/**
* Remove self from all dependencies' subscriber list.
* 从所有依赖项的订阅服务器列表中删除self。
*/
teardown () {
if (this.active) {
// remove self from vm's watcher list
// this is a somewhat expensive operation so we skip it
// if the vm is being destroyed.
// 从vm的观察者列表中删除自身
// 这是一个有点贵的手术,所以我们跳过它
// 如果虚拟机正在被销毁。
// 是否被销毁
if (!this.vm._isBeingDestroyed) {
// 删除观察者
remove(this.vm._watchers, this)
}
// 遍历删除
let i = this.deps.length
while (i--) {
this.deps[i].removeSub(this)
}
// 变成不活跃
this.active = false
}
}
}
array.js
/*
* not type checking this file because flow doesn't play well with
* dynamically accessing methods on Array prototype
*/
// 导入def 也就是 Object.defineProperty
import { def } from '../util/index'
// 复制一份 Array.prototype到arrayMethods
const arrayProto = Array.prototype
// arrarMethods是Array.proto的复制
export const arrayMethods = Object.create(arrayProto)
// 获取这7个数组方法,通过def拦截这7个方法,给它们增加副作用
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* Intercept mutating methods and emit events
* 拦截转换方法并发出事件
*/
// 将这7个方法遍历
methodsToPatch.forEach(function (method) {
// cache original method
// 从原型中把原始方法拿出,在后面会调用一次原始方法,
// 并在原始方法的上增加副作用
const original = arrayProto[method]
// 额外通知更新 def相当于Object.defineProperty
// 给arrayMehods的method方法定义一个函数mutator
// 就是在执行push pop等方法的基础上干一些额外的事
// 也就是下面的ob.dep.notify()通知改变
def(arrayMethods, method, function mutator (...args) {
// 执行数组方法原本应该做的事情
const result = original.apply(this, args)
// 获取到这个数组的__ob__实例
const ob = this.__ob__
let inserted
// 这三个方法特殊,因为会对数组进行增加操作,之前数组所有元素都是已经
// 做过响应式了,所以要对新增加的元素再进行响应式处理
// 所以要通过inserted是否有值,对新增值的三个数组方法进行再次遍历响应式
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
// 如果有新增的值,也就是使用了push unshift splice三个方法
// 调用ob.observeArray,也就是遍历将数组所有元素进行observe
// 也就是说增加和删除元素,都还是会响应式
if (inserted) ob.observeArray(inserted)
// notify change
// 通知更新
ob.dep.notify()
// 最后返回数组方法原本操作的结果
return result
})
})
scheduler.js
/* @flow */
import type Watcher from './watcher'
import config from '../config'
import { callHook, activateChildComponent } from '../instance/lifecycle'
import {
warn,
nextTick,
devtools,
inBrowser
} from '../util/index'
export const MAX_UPDATE_COUNT = 100
const queue: Array<Watcher> = [] // 记录观察者队列的数组
const activatedChildren: Array<Component> = [] //记录活跃的子组件
let has: { [key: number]: ?true } = {} //记录观察者的id
let circular: { [key: number]: number } = {} // 持续循环更新的次数,如果超过100次 则判断已经进入了死循环,则会报错
let waiting = false //观察者在更新数据时候 等待的标志
let flushing = false //进入flushSchedulerQueue 函数等待标志
let index = 0 //queue 观察者队列的索引
/**
* Reset the scheduler's state.
* 重置计划程序的状态
* 也就是清空观察者watcher队列中所有数据
*/
function resetSchedulerState () {
// 观察队列长度和活跃子组件长度都变为0
index = queue.length = activatedChildren.length = 0
// 观察者记录的id
has = {}
if (process.env.NODE_ENV !== 'production') {
circular = {}
}
// 两个等待标志设为false
waiting = flushing = false
}
// Async edge case #6566 requires saving the timestamp when event listeners are
// attached. However, calling performance.now() has a perf overhead especially
// if the page has thousands of event listeners. Instead, we take a timestamp
// every time the scheduler flushes and use that for all event listeners
// attached during that flush.
// 异步边缘情况要求在附加事件侦听器时保存时间戳
// 但是,当performance.now()现在的性能开销很大,如果页面有上千个事件监听器
// 相反,我们在每次调度程序刷新时获取一个时间戳,并将其用于刷新期间附加的所有事件侦听器
// 调度程序刷新时获取的时间戳
export let currentFlushTimestamp = 0
// Async edge case fix requires storing an event listener's attach timestamp.
// 异步边缘情况修复需要存储事件侦听器的附加时间戳。
// 获取当前时间戳
let getNow: () => number = Date.now
// Determine what event timestamp the browser is using. Annoyingly, the
// timestamp can either be hi-res (relative to page load) or low-res
// (relative to UNIX epoch), so in order to compare time we have to use the
// same timestamp type when saving the flush timestamp.
// 确定浏览器正在使用的事件时间戳。
// 令人恼火的是,时间戳可以是高分辨率(相对于页面加载)或低分辨率(相对于UNIX epoch)
// 所以为了比较时间,我们在保存刷新时间戳时必须使用相同的时间戳类型。
if (
inBrowser && //如果是浏览器
window.performance && //如果performance存在
typeof performance.now === 'function' && // 如果performance.now是函数
document.createEvent('Event').timeStamp <= performance.now() //如果时间戳小于现在
) {
// if the event timestamp is bigger than the hi-res timestamp
// (which is evaluated AFTER) it means the event is using a lo-res timestamp,
// and we need to use the lo-res version for event listeners as well.
// 如果事件时间戳大于高分辨率时间戳(之后计算),则表示事件使用低分辨率时间戳,
// 我们还需要为事件侦听器使用lores版本。
// performance.now()是当前时间与performance.timing.navigationStart的时间差,
// 以微秒(百万分之一秒)为单位的时间,与 Date.now()-performance.timing.navigationStart
// 的区别是不受系统程序执行阻塞的影响,因此更加精准。
getNow = () => performance.now()
}
/**
* Flush both queues and run the watchers.
* 刷新两个队列并运行观察程序
* 更新观察者,运行watcher.run(),并且调用组件更新和激活的钩子
*/
function flushSchedulerQueue () {
// 获取当前时间戳,可能以Date.now或者performance.now获取
currentFlushTimestamp = getNow()
// 然后进入flushSchedulerQueue 函数等待标志位true
flushing = true
let watcher, id
// Sort queue before flush.
// This ensures that:
// 1. Components are updated from parent to child. (because parent is always
// created before the child)
// 2. A component's user watchers are run before its render watcher (because
// user watchers are created before the render watcher)
// 3. If a component is destroyed during a parent component's watcher run,
// its watchers can be skipped.
// 刷新前对队列排序
// 这样可以确保:
// 1. 组件从父级更新到子级。(因为父对象总是在子对象之前创建)
// 2. 组件的用户观察程序在其渲染观察程序之前运行(因为用户观察程序是在渲染观察程序之前创建的)
// 3. 如果某个组件在父组件的观察程序运行期间被破坏,则可以跳过它的观察程序。
// 刷新前对队列进行排序 根据id排序
queue.sort((a, b) => a.id - b.id)
// do not cache length because more watchers might be pushed
// as we run existing watchers
// 当我们运行现有的观察者时,不要缓存长度,因为可能会推送更多观察者
// 遍历观察者数组
for (index = 0; index < queue.length; index++) {
// 获取单个观察者
watcher = queue[index]
// 如果存在before
if (watcher.before) {
watcher.before()
}
// 获取id
id = watcher.id
has[id] = null
// 运行观察者
watcher.run()
// in dev build, check and stop circular updates.
// 在dev build中,检查并停止循环更新。
if (process.env.NODE_ENV !== 'production' && has[id] != null) {
circular[id] = (circular[id] || 0) + 1
if (circular[id] > MAX_UPDATE_COUNT) {
warn(
'You may have an infinite update loop ' + (
watcher.user
? `in watcher with expression "${watcher.expression}"`
: `in a component render function.`
),
watcher.vm
)
break
}
}
}
// keep copies of post queues before resetting state
// 在重置状态之前保留投递队列的副本 都是浅拷贝
const activatedQueue = activatedChildren.slice()
const updatedQueue = queue.slice()
// 清空观察者队列
resetSchedulerState()
// call component updated and activated hooks
// 调用组件更新并激活钩子函数
callActivatedHooks(activatedQueue)
callUpdatedHooks(updatedQueue)
// devtool hook
/* istanbul ignore if */
// 触发父层flush事件钩子函数
if (devtools && config.devtools) {
devtools.emit('flush')
}
}
// 触发 updated生命周期钩子函数
function callUpdatedHooks (queue) {
// 获取观察者队列长度
let i = queue.length
// 遍历
while (i--) {
const watcher = queue[i]
// 获取到虚拟dom
const vm = watcher.vm
// 如果有watcher 并且 已经mounted并且没被Destroyed
if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
// 触发updated生命周期钩子函数
callHook(vm, 'updated')
}
}
}
/**
* Queue a kept-alive component that was activated during patch.
* The queue will be processed after the entire tree has been patched.
*
* 对修补期间激活的保持活动状态的组件进行排队。
* 将在修补整个树之后处理队列。
*
* 添加活跃的组件函数,把活跃的vm添加到activatedChildren中
*/
export function queueActivatedComponent (vm: Component) {
// setting _inactive to false here so that a render function can
// rely on checking whether it's in an inactive tree (e.g. router-view)
// 在这里将“inactive”设置为false,以便呈现函数可以
// 依靠检查它是否在不活动的树中(例如路由器视图)
// 将_inactive设为false,然后加入到activatedChildren,记录活跃子组件队列中
vm._inactive = false
activatedChildren.push(vm)
}
// 调用组件激活的钩子
function callActivatedHooks (queue) {
// 遍历观察者队列
for (let i = 0; i < queue.length; i++) {
// 所有置为true
queue[i]._inactive = true
//判断是否有不活跃的组件 禁用他 如果有活跃组件则触发钩子函数activated
activateChildComponent(queue[i], true /* true */)
}
}
/**
* Push a watcher into the watcher queue.
* Jobs with duplicate IDs will be skipped unless it's
* pushed when the queue is being flushed.
*
* 将观察者推入观察者队列。
* 具有重复ID的作业将被跳过,除非在刷新队列时推送。
*
* 将观察者watcher推进到观察者队列中,过滤重复id,除非是刷新队列时推送
*/
export function queueWatcher (watcher: Watcher) {
// 获取id
const id = watcher.id
if (has[id] == null) {
has[id] = true
// 如果进入flushSchedulerQueue 函数等待标志 为false
if (!flushing) {
// 把观察者添加到队列中
queue.push(watcher)
} else {
// if already flushing, splice the watcher based on its id
// if already past its id, it will be run next immediately.
// 如果已经刷新,则根据其id拼接观察程序
// 如果已经超过了它的id,它将立即运行。
let i = queue.length - 1
while (i > index && queue[i].id > watcher.id) {
i--
}
//根据id大小拼接插入在数组的哪个位置
queue.splice(i + 1, 0, watcher)
}
// queue the flush
// 观察者在更新数据时候 等待的标志 为false
if (!waiting) {
waiting = true
if (process.env.NODE_ENV !== 'production' && !config.async) {
// 刷新两个队列并运行观察程序
// 更新观察者,运行watcher.run(),并且调用组件更新和激活的钩子
flushSchedulerQueue()
return
}
// 更新观察者 运行观察者watcher.run() 函数 并且调用组件更新和激活的钩子
// 异步清空回调函数队列
nextTick(flushSchedulerQueue)
}
}
}
traverse.js
/* @flow */
import { _Set as Set, isObject } from '../util/index'
import type { SimpleSet } from '../util/index'
import VNode from '../vdom/vnode'
// seenObjects是Set实例
const seenObjects = new Set()
/**
* Recursively traverse an object to evoke all converted
* getters, so that every nested property inside the object
* is collected as a "deep" dependency.
* 递归地遍历一个对象以唤起所有已转换的
* getter,以便对象内的每个嵌套属性
* 作为“深层”依赖关系收集。
* 为 seenObjects深度收集val中的key
*/
export function traverse (val: any) {
// 为seenObjects深度收集val中的key
_traverse(val, seenObjects)
seenObjects.clear()
}
// 为seenObjects深度收集val中的key
function _traverse (val: any, seen: SimpleSet) {
let i, keys
// 是否是数组
const isA = Array.isArray(val)
// 如果不是数组并且不是对象或被冻结 或是Vnode实例
if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {
返回
return
}
// 如果val存在__ob__属性
if (val.__ob__) {
// 获取__ob__的dep.id
const depId = val.__ob__.dep.id
// 如果seenObjects中有这个depId
if (seen.has(depId)) {
// 返回
return
}
// 如果没有这个depId,给seenObjects这个set添加一个depId
seen.add(depId)
}
// 如果是数组
if (isA) {
i = val.length
// 遍历所有值,进行递归检查添加
while (i--) _traverse(val[i], seen)
} else {
// 如果不是数组,获取所有key
keys = Object.keys(val)
i = keys.length
// 遍历对象的所有key进行循环递归检查添加
while (i--) _traverse(val[keys[i]], seen)
}
}