Vue2 初始化实例过程源码解析(三)

138 阅读1分钟

本文接着上一节继续来阅读Vue源码初始化内容

关键性代码

import { initProxy } from './proxy'
import { initState } from './state'
import { initRender } from './render'
import { initEvents } from './events'
import { initLifecycle, callHook } from './lifecycle'
import { initProvide, initInjections } from './inject'
import { extend, mergeOptions, formatComponentName } from '../util/index'

export function initMixin (Vue) {
  Vue.prototype._init = function (options) {
    const vm = this
    vm._uid = uid++

    vm._isVue = true
    
    if (options && options._isComponent) {
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    
    vm._renderProxy = vm
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm)
    initState(vm)
    initProvide(vm)
    callHook(vm, 'created')

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

1. hook调用

callHook(vm, 'beforeCreate')

Vue的生命周期大家应该都很熟悉了,主要的事件执行机制可以从这里开始了解。 首先进入该函数。

export function callHook (vm, hook) {
  pushTarget()
  // 取出在该hook下的所有内容,这会是一个函数数组
  const handlers = vm.$options[hook]
  const info = `${hook} hook`
  // 执行数组中的每一个方法
  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      // 具有错误处理功能的函数调用
      invokeWithErrorHandling(handlers[i], vm, null, vm, info)
    }
  }
  // 触发事件
  if (vm._hasHookEvent) {
    vm.$emit('hook:' + hook)
  }
  popTarget()
}

注意这行语句

const handlers = vm.$options[hook]

1.1 mergeHook

这显然是挂载在实例options上的内容,可以在src/shared/constants.js中看到LIFECYCLE_HOOKS,里面有我们熟悉的所有钩子函数名称。搜索后可以在src/core/util/options.js中找到相关内容。

LIFECYCLE_HOOKS.forEach(hook => {
  strats[hook] = mergeHook
})

function mergeHook (parentVal, childVal) {
  const res = childVal 
      ? parentVal
          ? parentVal.concat(childVal) : Array.isArray(childVal)
              ? childVal : [childVal] : parentVal
  return res ? dedupeHooks(res) : res
}

function dedupeHooks (hooks) {
  const res = []
  for (let i = 0; i < hooks.length; i++) {
    if (res.indexOf(hooks[i]) === -1) {
      res.push(hooks[i])
    }
  }
  return res
}

相对来说,就是把hooks都合并到相应名称的数组下

1.2 pushTarget/popTarget

export function pushTarget (target: ?Watcher) {
  targetStack.push(target)
  Dep.target = target
}

export function popTarget () {
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]
}

简单说一下这两个函数的作用。Vue响应式最重要的是进行依赖收集。而只有当Dep.target存在时,才会进行依赖收集。hook函数作用时不需要进行,因此pushTarget没有传值,将Dep.target置空即可。

2. initInjections 初始化inject

export function initInjections (vm) {
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    // 是否响应式处理,false为不处理
    toggleObserving(false)
    Object.keys(result).forEach(key => {
      defineReactive(vm, key, result[key])
    })
    toggleObserving(true)
  }
}

2.1 resolveInject

export function resolveInject (inject, vm) {
  if (inject) {
    const result = Object.create(null)
    // 获取inject的所有key
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)
    // 遍历
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      if (key === '__ob__') continue
      // 标准化后的inject[key]中一定会有from参数
      const provideKey = inject[key].from
      let source = vm
      // 遍历祖先组件,直到找到相应的provideKey
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      // 如果祖先组件未找到provideKey,启用default选项
      if (!source) {
        if ('default' in inject[key]) {
          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        }
      }
    }
    return result
  }
}

3. initState

export function initState (vm) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

可看到由上至下分别初始化propsmethodsdatacomputedwatch,如果产生重名冲突,初始化在前优先级更高

3.1 initProps

function initProps (vm, propsOptions) {
  const propsData = vm.$options.propsData || {}
  const props = vm._props = {}
  // 创建并维护一个存入所有key的数组
  const keys = vm.$options._propKeys = []
  const isRoot = !vm.$parent
  // root instance props should be converted
      // 根实例的props需要响应式处理
  if (!isRoot) {
    toggleObserving(false)
  }
  for (const key in propsOptions) {
    keys.push(key)
    // prop验证
    const value = validateProp(key, propsOptions, propsData, vm)
    // 响应式处理关键函数
    defineReactive(props, key, value)
    // 代理到组件的_props_上
    if (!(key in vm)) {
      proxy(vm, `_props`, key)
    }
  }
  toggleObserving(true)
}

4.2 initMethods

function initMethods (vm: Component, methods: Object) {
  const props = vm.$options.props
  直接把相关函数挂载到组件实例下
  for (const key in methods) {
    vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
  }
}

3.3 initData

function initData (vm) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    // getData 进行错误处理,不做深入了解了
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
  }
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    // 如果key没有在props和methods并且不是保留值,进行代理
    proxy(vm, `_data`, key)
  }
  // 响应式处理
  observe(data, true /* asRootData */)
}

3.4 initComputed

function initComputed (vm, computed) {
  const watchers = vm._computedWatchers = Object.create(null)

  for (const key in computed) {
    const userDef = computed[key]
    // getter的默认方式是fuction,但也可以直接设置get,set
    const getter = typeof userDef === 'function' ? userDef : userDef.get

    if (!isSSR) {
      // 如果不是服务端渲染,进行响应式处理,添加到watchers中
      watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions)
    }

    if (!(key in vm)) {
      defineComputed(vm, key, userDef)
    }
  }
}
3.4.1 defineComputed
export function defineComputed (target, key, userDef) {
  // 服务端渲染不缓存
  const shouldCache = !isServerRendering()
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      // 缓存处理
      ? createComputedGetter(key)
      // 直接调用
      : createGetterInvoker(userDef)
    sharedPropertyDefinition.set = noop
  } else {
    sharedPropertyDefinition.get = userDef.get
      // 处理用户设置的cache
      ? shouldCache && userDef.cache !== false
        ? createComputedGetter(key)
        : createGetterInvoker(userDef.get)
      : noop
    sharedPropertyDefinition.set = userDef.set || noop
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

function createGetterInvoker (fn) {
  return function computedGetter () {
    return fn.call(this, this)
  }
}

// 默认值,便于后续设置get,set
const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}
3.4.2 createComputedGetter
function createComputedGetter (key) {
  return function computedGetter () {
    // 获取到挂载在实例上的watcher
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      if (watcher.dirty) {
        // 调用evaluate重新获取属性值
        watcher.evaluate()
      }
      if (Dep.target) {
        // 依赖收集
        watcher.depend()
      }
      return watcher.value
    }
  }
}

3.5 initWatch

function initWatch (vm, watch) {
  for (const key in watch) {
    const handler = watch[key]
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}
3.5.1 createWatcher
function createWatcher (vm, expOrFn, handler, options) {
  // 获取到watcher的具体函数并传给vm.$watch
  // 如果是对象形式,就获取它的handler
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  // 如果是一个字符串,直接在vm上获取
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}
3.5.2 vm.$watch
  Vue.prototypevm.$watch = function (expOrFn, cb, options) {
    const vm = this
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    // 用户级watcher
    options.user = true
    // 设置响应式
    const watcher = new Watcher(vm, expOrFn, cb, options)
    // 有immediate参数需要立即执行
    if (options.immediate) {
      const info = `callback for immediate watcher "${watcher.expression}"`
      pushTarget()
      invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
      popTarget()
    }
    return function unwatchFn () {
      watcher.teardown()
    }
  }
}

4. initProvide

// 解析配置项的provide,并挂载到_provide选项
export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

本节我们延续之前的内容,讲述了hook调用的原理,以及inject,provide的初始化,当然还有最重要的state,按顺序分别是props,methods,data,computed以及watcher的初始化。下一节,我会带大家一起看一下Vue中最重要的响应式处理机制。