# Vue 初始化

# Vue 的构造函数

/src/core/instance/init.js

  • Vue_init方法大致
    1. 每个 Vue 实例,会将所有的属性挂载到vm.$options
    2. 初始化组件实例关系,定义 prototype 方法。例:$mount_render_update ...。初始化自定义事件vm._events
    3. 解析插槽信息得到 vm.$slotvm.$scopedSlots...。
    4. 使用vm.$mount函数构建 DOM。(如果没有使用 loadertemplate-compiler ,会改写vm.$mount使之生成template对应的render函数)









































 
 
 
 
 
 
 
 







export class Vue {
  constructor(options?: Object) {
    const vm: Component = this
    // 每个 Vue 实例上的 _uid,依次递增, 唯一
    vm._uid = uid++
    // 代表当前实例已经被 observed
    vm._isVue = true
    // 处理组件配置项
    if (options && options._isComponent) {
      /**
       * _isComponent 估计 components 属性或者 router-view 中会赋值为 true
       * 每个子组件初始化走这,做了一些性能优化
       * 将每个实例上的深层次属性放到 $options 选项中,以提高代码的执行效率
      */
      initInternalComponent(vm, options);
  } else {
      // 初始化根组件时走这里,合并 Vue 的全局配置到根组件的局部配置
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    
    if (process.env.NODE_ENV !== 'production') {
      // 设置代理,将vm实例上的属性代理到vm._renderProxy
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    
    // expose(暴露) real self
    vm._self = vm
    // 初始化组件实例关系属性,如 $parent、$children、$root、$refs 等
    initLifecycle(vm)
    // 初始化自定义事件
    initEvents(vm)
    // 解析插槽信息,得到 $slots,$createElement
    initRender(vm)
    // 调用 beforeCreate 钩子函数
    callHook(vm, 'beforeCreate')
    // 初始化组件的 inject 配置型,得到 result[key] = val 形式的配置对象,然后对数据进行响应式处理,然后将每个key挂载到自身实例上
    initInjections(vm)
    // 处理响应式数据。props,data,computed,watch,methods
    initState(vm);
    // 解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上
    initProvide(vm)
    // 调用created钩子函数
    callHook(vm, 'created')
    // 如果发现配置项上有el选项,则自动调用$mount方法
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }   
}

# initInjections

/src/core/instance/inject



 










export function initInjections (vm: Component) {
  // 解析 inject 配置项,然后从祖代组件的配置中找到 配置项中每一个 key 对应的 value
  const result = resolveInject(vm.$options.inject, vm)
  // 对获得 inject 对象挂载到 this 上,同时做数据响应处理
  if (result) {
    toggleObserving(false);
    Object.keys(result).forEach(key => {
      defineReactive(vm, key, result[key])
    })
    toggleObserving(true);
  }
}

# resolveInject

/src/core/instance/inject






















 


 
 
 
 
 
 
 
 



 
 
 
 
 








// inject 对象在合并选项是对组件配置做了标准化处理,所以 inject 的结构是一样的
interface Inject {
  [key: string]: {
    from: string
    default?: unknown
  }
}

export function resolveInject (inject: Inject, vm: Component): Object | void {
  if (inject) {
    const result = Object.create(null)
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)
    
    // 遍历
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // 跳过 __ob__ 对象
      if (key === '__ob__') continue
      // 处理 form 属性获取 provideKey
      const provideKey = inject[key].form
      let source = vm
      // 遍历所有祖代实例,找到最近的祖代实例中的 provide 中对应 key 的值
      while (source) {
        if (source.provide && hasOwn(source._provide, provideKey)) {
          // 在 initProvide 函数中会将 provide 对象,将其挂载在 source._provided 属性上
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
    }
    // 如果没找到对应的 inject, 则使用 default 属性
    if (!source) {
      if ('default' in inject[key]) {
        const proviedeDefault = inject[key].default
        result[key] = provideDefault === 'function'
          ?  provideDefault.call(vm)
          : provideDefault
      } else {
        throw new Error('error')
      }
    }
    return result
  }
}

# initState

/src/core/instance/state

  • 依顺序分别处理
    • initProps: 为props各个属性实现响应式
    • initMethods: 保证methods中都必须是一个函数,同时key不会和其他属性冲突
    • initData: 进行observe处理,进行依赖收集
    • initComputed: 为computed每个属性创建懒加载 Wathcer
      1. 通过Watcherget()获取值,并将得到的值缓存与 Wathcervalue属性(dirtyfalse)。
      2. 通过definePeoperty代理返回返回watcher.value
      3. 当它依赖的数据发生改变。 会将自身的 Watcherdirty属性改为 true。此时会重新再运行get()函数,并收集依赖。
    • initWatch: 将 Watcher 都放入到vm._watchers数组中




 

 


 


 


 





 



export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  // 处理 props
  if (opts.props) initProps(vm, opts.props)
  // 处理 methods
  if (opts.methods) initMethods(vm, opts.methods)
  // 处理 data,所以在 data 里能访问 props,但是拜托希望别这么用
  if (opts.data) {
    initData(vm)
  } else {
    // 放在下一章节
    observe(vm._data = {}, true /* asRootData */)
  }
  // 处理 computed
  if (opts.computed) initComputed(vm, opts.computed)
  /**
   * 处理 watch,为每个 key 创建 watcher 实例,key 与 watcher 实例可能是一对多的管理
   * 如果配置了 immediate 则会立即执行回调函数
  */
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

# initProps

  • 处理props对象,为每个属性设置响应式,并将其代理到实例 vm 上
export function initProps (vm: Component, propsOptions: Object) {
  const propsData = vm.$options.propsData || {}
  const props = vm._props = {}
  // 缓存 props 的每个key,性能优化
  const keys = vm.$options._propKeys = []
  const isRoot = !vm.$parent
  // 因为根组件的 props 是不会改变的 所以不需要收集根组件的 props 的依赖
  if (!isRoot) {
    toggleObserving(false)
  }
  for (const key in propsOptions) {
    keys.push(key)
    // 获取 props[key] 的正真的值
    // propsOptions = { value: { type: string, default: '' } }
    const value = validateProp(key, propsOptions, propsData, vm)
    // 为每个属性设置响应式
    defineReactive(props, key, value)
    if (!(key in vm)) {
      proxy(vm, `_props`, key)
    }
  }
  toggleObseving(true)
}

# initMethods

export function initMethods (vm: Component, methods: Object) {
  // initProps 是由 $options.propsData 取,这里从 props 取是为了防止触发 props 的 get 与 set
  const props = vm.$options.props
  // 遍历 methods 放置 key 重复
  for (const key in methods) {
    if (process.env.NODE_ENV !== 'production') {
      if (typeof methods[key] !== 'function') {
        warn(/* ... */)
      }
      if (props && hasOwn(props, key)) {
        warn(/* key与props中key重复 */)
      }
      if ((key in vm) && isReserved(key)) {
        warn(/* key与vue中定义的属性相同 */)
      }
    }
    vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
  }
}

# initData

export function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? 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]
    // 省略与 props 和 methods 中 key 的去重处理
    observe(data, true)
  }
}

# initComputed

  • 为所有 computed[key] 创建 watcher 实例,默认是懒执行。





















 













 

































export function initComputed (vm: Component, computed: Object) {
  const wathcers = vm._computedWatchers = Object.create(null)
  // SSR 服务端渲染
  const isSSR = isServerRendering()
  
  // 遍历computed 
  for (const key in computed) {
    // 获取对应的 getter 函数
    const userDef = computed[key]
    const getter = typeof userDef === 'function' ? userDef: userDef.get
    if (getter === null) warn(/* error */)
    if (!isSSR) {
      watchers[key] = new Watcher(
        vm,
        getter || noop,
        noop,
        computedWatcherOptions // 默认是懒执行
      )
    }
    
    if (!(key in vm)) {
      defineComputed(vm, key, userDef)
    } else {
      warn(/* 重复 */)
    }
  }
}

function defineComputed (target: any, key: string, userDef: Object | Function) {
  if (typeof userDef === 'funciton') {
    /* 如果是服务端渲染会进行createGetterInvoker(userDef)处理 */
    sharedPropertyDefinition.get = createComputedGetter(key)
  } else {
    // 一般用户定义的是不会指定 cache 属性, 如果 cache: false,则会创建空的 getter 函数
    sharedPropertyDefinition.get = userDef.get && userDef.cache !== false
      ? createComputedGetter(key)
      : noop
    sharedPropertyDefinition.set = userDef.set || noop     
  }
  // 如果没有设置 set 会进行warn
  // 绑定
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

function createComputedGetter (key) {
  // computed 缓存, 当获取值时会触发 get 方法
  return function computedGetter () {
    // 通过key 去寻找对应的 Watcher
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (wathcer) {
      /**
       * computed 中被引用的值发生变化的时候
       * 会将 computed 的 watcher 的 dirty 属性设为 true
       * 代表需要重新调用一次 handler 函数来得到最新的值
       * 否则直接返回缓存的 watcher.value
      */
      if (watcher.dirty) {
        // evaluate 之后会再次将 dirty 设为 false
        watcher.evaluate()
      }
      // 依赖收集
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }
}

# initWatch

  • 处理watch对象,通过vm.$watch为每个属性创建一个Watcher
// watch的几种使用方式
export default {
  watch: {
    key1: function (newVal, oldVal) { /* handler */ },
    key2: {
      handler (newVal, oldVal) { /* handler */ },
      deep: true,
      immediate: true
    },
    key3: [
      this.methodName,
      fucntion() { /* handler */ },
      {
        handler() { /* handler */ },
        deep: false
      }
    ],
    'key4': 'methodsName', // vm 上的 methods 方法名
    'props.key5': {
      handler (newVal, oldVal) { /* handler */ }
    }
  }
}







 


 



















 


export function initWatch (vm: Component, watch: Object) {
  // 为每个 key 都调用 createWatcher
  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)
    }
  }
}

function createWatcher (
  vm: Component,
  exOrFn: string | Function
  handler: any
  options?: Object
) {
  // 如果 handler 为对象,则获取其中的 handler 选项的值
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  // 如果为 string,则获取 this[handler] 方法
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}

# vm.$watcher

Watcher对象














 














Vue.prototype.$watch = function (
  expOrFn: string | Function,
  cb: any,
  options?: Object
) {
  const vm: Component = this
  // 是对象就处理成函数再进行处理
  if (isPlainObject(cb)) {
    return createWatcher(vm, expOrFn, cb, options)
  }
  
  options = options || {}
  options.user = true // 标记 此用户创建的 wathcer
  const watcher = new Watcher(vm, expOrFn, cb, options)
  // 如果设置了 immediate 则立即执行回调函数
  if (options.immdiate) {
    try {
      cb.call(vm, wathcer.value)
    } catch (err) {
      // doSomthing...
    }
  }
  // 用来解除监听
  return function unwatchFn () {
    watcher.teardown()
  }
}

# initProvide

  • /src/core/instance/inject
  • provide基本属性值是非响应式的
// 将 provide 对象,将其挂载在 vm._provided 属性上
export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    // provide 属性可以是一个函数,也可以是一个对象
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide 
  }
}