vue源码(十) 进行响应式前的准备大纲以及Observer文件夹逐行注释

22 篇文章 7 订阅
19 篇文章 9 订阅

今日达成:大纲准备完毕

大纲

准备花4天将这些整理完毕,分享给大家,因此下一期将在4天后!这里先把所有文件的注释奉上

  • Object.defineProperty
  • 发布订阅模式
  • Observer类方法
    • 构造函数内部实现数组和对象的响应式
      • protoAugment
      • copyAugment
      • array.js 数组方法响应式
    • walk
    • observerArray
  • defineReactive
    • 深层遍历 childOb = !shallow && observe(val)
    • get方法
      • dep.depend()
      • 深层遍历 childOb.dep.depend()
      • 数组特殊处理 dependArray(value)
    • set方法
      • dep.notify
  • 分析mountComponet时注册的watcher,给下面的watcher传参
  • Dep
    • depend
    • notify
  • pushTarget
  • popTarget
  • Watcher
    • get
    • update 更新
    • addDep避免重复收集依赖
    • cleanupDeps 清除一次的值
    • run
    • options分析
  • 依赖收集
    • this.get
    • pushTarget
    • this.getter.call
    • dep.depend
  • 通知更新
    • dep.notify
    • run
    • queuewathcer
      • nextTick
  • scheduler分析 quequeWatcher函数所在文件
  • travse分析 深层遍历

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中收集依赖项并发送更新。
 */

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)
  }
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值