Vue学习笔记

115 阅读4分钟

Vue 初始化过程

...
merge options 
...
//合并配置选项options

initLifecycle(vm) 
//初始化组件实例关系属性,比如 $parent、$children、$root、$refs 等

initEvents(vm) 
//初始化自定义事件,注意:这里的事件是值在父组件在子组件上定义的事

initRender(vm) 
//解析组件的插槽信息,得到 vm.$slot,处理渲染函数,
// 得到 vm.$createElement方法,即 h 函数

callHook(vm, 'beforeCreate') 
//调用 beforeCreate 钩子函数

initInjections(vm) // resolve injections before data/props
// 初始化组件的 inject 配置项,得到 result[key] = val 形式的配置对象,
// 然后对结果数据进行响应式处理,并代理每个 key 到 vm 实例

initState(vm)
// 初始化 props、methods、data、computed、watch(按照此顺序)

initProvide(vm) // resolve provide after data/props
// 解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上

callHook(vm, 'created')
//调用 create 钩子函数

if (vm.$options.el) {
  vm.$mount(vm.$options.el)
}
// 如果存在el,则挂载

Vue数据响应式原理

image.png

initState: 数据响应式入口,分别处理 InitProps、initMethods、initData、initComputed、initWatch

- 其中,响应式核心函数:defineReactive(obj, key, value),为每个key设置响应式.
每个key拥有一个dep,在getter读取obj[key]时进行拦截,收集Watcher,在setter更新数据时
通过dep.notify()通知dep中的watcher更新,触发重新渲染。
/**
* 拦截 obj[key] 的读取和设置操作:
*   1、在第一次读取时收集依赖,比如执行 render 函数生成虚拟 DOM 时会有读取操作
*   2、在更新时设置新值并通知依赖更新
*/
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // 实例化 dep,一个 key 一个 dep
  const dep = new Dep()

  // 获取 obj[key] 的属性描述符,发现它是不可配置对象的话直接 return
  // Object.getOwnPropertyDescriptor 返回指定对象上一个自有属性对应的属性描述符。
  //(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // 记录 getter 和 setter,获取 val 值
  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }

  // 递归调用,处理 val 即 obj[key] 的值为对象的情况,保证对象中的所有 key 都被观察
  let childOb = !shallow && observe(val)
  // 响应式核心
  // Object.defineProperty: 直接在一个对象上定义一个新属性,
  // 或者修改一个对象的现有属性,并返回此对象。
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    // get 拦截对 obj[key] 的读取操作
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      /**
       * Dep.target 为 Dep 类的一个静态属性,值为 watcher,在实例化 Watcher 时会被设置
       * 实例化 Watcher 时会执行 new Watcher 时传递的回调函数(computed 除外,因为它懒执行)
       * 而回调函数中如果有 vm.key 的读取行为,则会触发这里的 读取 拦截,进行依赖收集
       * 回调函数执行完以后又会将 Dep.target 设置为 null,避免这里重复收集依赖
       */
      if (Dep.target) {
        // 依赖收集,在 dep 中添加 watcher,也在 watcher 中添加 dep
        dep.depend()
        // childOb 表示对象中嵌套对象的观察者对象,如果存在也对其进行依赖收集
        if (childOb) {
          // 这就是 this.key.chidlKey 被更新时能触发响应式更新的原因
          childOb.dep.depend()
          // 如果是 obj[key] 是 数组,则触发数组响应式
          if (Array.isArray(value)) {
            // 为数组项为对象的项添加依赖
            dependArray(value)
          }
        }
      }
      return value
    },
    // set 拦截对 obj[key] 的设置操作
    set: function reactiveSetter (newVal) {
      // 旧的 obj[key]
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      // 如果新老值一样,则直接 return,不更新更不触发响应式更新过程
      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 不存在说明该属性是一个只读属性,直接 return
      if (getter && !setter) return
      // setter存在,设置新值
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      // 对新值进行观察,让新值也是响应式的
      childOb = !shallow && observe(newVal)
      // 依赖通知更新
      dep.notify()
    }
  })
}

异步更新队列

  1. dep.notify(): 通知dep中所有的 watcher 执行 watcher.update()
  2. watcher.update(): 执行queueWatcher(this), 将自身放入到一个全局 watcher 队列,如果已经存在于队列中则跳过。
  3. nextTick(flushSchedulerQueue),利用浏览器异步更新队列刷新 watcher s。
  4. flushSchedulerQueue:

首先对queue进行排序:a.保证父组件先于子组件更新(因为父组件总是在子组件之前创建); b.如果一个组件在其父组件的 watcher 执行期间被销毁,则它的 watcher 可以被跳过; c.一个组件的用户 watcher 在其渲染 watcher 之前被执行,因为用户 watcher 先于 渲染 watcher 创建。然后执行watcher.run()函数,触发更新,比如updateComponent。

  1. nextTick(flushSchedulerQueue):

将flushSchedulerQueue函数放入全局callbacks数组中, 定义flushCallbacks()函数(作用是执行并清空callbacks数组),并将flushCallbacks放入浏览器的异步更新队列中,放入队列的优先级如下Promise > MutationObserver > setImmediate > setTimeout