咱们应用vue-cli搭建vue 2.x我的项目时,大抵由如下代码来做一个vue利用的初始化:

import Vue from "vue";import App from "./App.vue";Vue.config.productionTip = false;new Vue({  render: (h) => h(App),}).$mount("#app");

咱们能够就从此处开始对Vue的意识。能够看到,这里外表上只做了一个简略的工作,就是通过new操作创立了一个vue的实例,并传递了一个配置项对象,该对象蕴含了一个render办法。

依据这个调用,咱们找到src/core/instance/index.js文件,内容如下:

// src/core/instance/index.jsimport { initMixin } from './init'import { stateMixin } from './state'import { renderMixin } from './render'import { eventsMixin } from './events'import { lifecycleMixin } from './lifecycle'import { warn } from '../util/index'function Vue (options) {  if (process.env.NODE_ENV !== 'production' &&    !(this instanceof Vue)  ) {    warn('Vue is a constructor and should be called with the `new` keyword')  }  this._init(options)}initMixin(Vue)stateMixin(Vue)eventsMixin(Vue)lifecycleMixin(Vue)renderMixin(Vue)export default Vue

内容也很直观,这里定义了一个只承受new结构调用的Vue Function,并对Vue进行了一系列的混入操作。

再浅显地看一下这些Mixin都做了什么,能够看到是往Vue的prototype对象上挂了一些属性和办法。

大抵如下:

Vue.prototype|- initMixin    |- _init(options?: Object)|- stateMixin    |- $data    |- $props    |- $set(target: Array<any> | Object, key: any, val: any): any   <- ../observer/index    |- $delete(target: Array<any> | Object, key: any)               <- ../observer/index    |- $watch(expOrFn: string | Function, cb: any, options?: Object): Function|- eventMixin    |- $on(event: string | Array<string>, fn: Function): Component    |- $once(event: string, fn: Function): Component    |- $off(event?: string | Array<string>, fn?: Function): Component    |- $emit(event: string): Component|- lifecycleMixin    |- $_update(vnode: VNode, hydrating?: boolean)    |- $forceUpdate()    |- $destrouy()|- renderMixin    |- $nextTick(fn: Function)    |- _render(): VNode

Vue的函数体中,调用了一个_init的办法,并将参数传入,能够看到,_init办法是在initMixin中定义的。

持续看_init办法的定义:

// src/core/instance/init.jsVue.prototype._init = function (options?: Object) {    const vm: Component = this    // a uid    vm._uid = uid++    let startTag, endTag    /* istanbul ignore if */    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {      startTag = `vue-perf-start:${vm._uid}`      endTag = `vue-perf-end:${vm._uid}`      mark(startTag)    }    // a flag to avoid this being observed    vm._isVue = true    // merge options    if (options && options._isComponent) {      // optimize internal component instantiation      // since dynamic options merging is pretty slow, and none of the      // internal component options needs special treatment.      initInternalComponent(vm, options)    } else {      vm.$options = mergeOptions(        resolveConstructorOptions(vm.constructor),        options || {},        vm      )    }    /* istanbul ignore else */    if (process.env.NODE_ENV !== 'production') {      initProxy(vm)    } else {      vm._renderProxy = vm    }    // expose real self    vm._self = vm    initLifecycle(vm)    initEvents(vm)    initRender(vm)    callHook(vm, 'beforeCreate')    initInjections(vm) // resolve injections before data/props    initState(vm)    initProvide(vm) // resolve provide after data/props    callHook(vm, 'created')    /* istanbul ignore if */    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {      vm._name = formatComponentName(vm, false)      mark(endTag)      measure(`vue ${vm._name} init`, startTag, endTag)    }    if (vm.$options.el) {      vm.$mount(vm.$options.el)    }}

见名知意,这个函数是对vue实例做一系列的初始化操作。

  1. 获取vue实例的结构器以及父级结构器(顺次递归)上的配置项,以及参数传递进来的配置项,在加上实例自带的属性,都合并到一起,挂在实例的$option属性身上
  2. 将vue实例本身挂在_renderProxy属性上
  3. 初始化数据和办法前做一些筹备工作

    1. initLifecycle:初始化生命周期
    2. initEvents:初始化事件
    3. initRender:初始化render
    4. 触发beforeCreate钩子
  4. 初始化数据和办法

    1. initInjections:解决$options.inject,对注入的数据做响应式解决
    2. initState做的几件事

      1. initProps:对$options.props做响应式解决
      2. initMethods:对$options.methods对象做解决,将所有的办法间接挂在实例对象上,并将办法的this绑定到vue实例对象vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
      3. initData:对$options.data进行observeobserve(data, true /* asRootData */),持续追踪能够看到observe办法是对data进行响应式解决,返回一个Observer实例

        // src/core/boserver/index.jsexport class Observer {  value: any;  dep: Dep;  vmCount: number; // number of vms that have this object as root $data  constructor (value: any) {    this.value = value    this.dep = new Dep()    this.vmCount = 0    def(value, '__ob__', this)    if (Array.isArray(value)) {      if (hasProto) {        protoAugment(value, arrayMethods)      } else {        copyAugment(value, arrayMethods, arrayKeys)      }      this.observeArray(value)    } else {      this.walk(value)    }  }  /**   * Walk through all properties and convert them into   * getter/setters. This method should only be called when   * value type is Object.   */  walk (obj: Object) {    const keys = Object.keys(obj)    for (let i = 0; i < keys.length; i++) {      defineReactive(obj, keys[i])    }  }  /**   * Observe a list of Array items.   */  observeArray (items: Array<any>) {    for (let i = 0, l = items.length; i < l; i++) {      observe(items[i])    }  }}
      4. initComputed:解决计算属性$options.computed

        给每个计算属性创立Watcher实例

        // src/core/instance/state.jsconst computedWatcherOptions = { lazy: true }function initComputed(vm: Component, computed: Object) {  // ...  const watchers = (vm._computedWatchers = Object.create(null))  // ...  const isSSR = isServerRendering()    for (const key in computed) {    const userDef = computed[key]    const getter = isFunction(userDef) ? userDef : userDef.get    if (__DEV__ && getter == null) {      warn(`Getter is missing for computed property "${key}".`, vm)    }    if (!isSSR) {      // create internal watcher for the computed property.      watchers[key] = new Watcher(        vm,        getter || noop,        noop,        computedWatcherOptions      )    }        if (!(key in vm)) {      defineComputed(vm, key, userDef)    }    // ...  }    // ...}export function defineComputed (  target: any,  key: string,  userDef: Object | Function) {  const shouldCache = !isServerRendering()  if (typeof userDef === 'function') {    sharedPropertyDefinition.get = shouldCache      ? createComputedGetter(key)      : createGetterInvoker(userDef)    sharedPropertyDefinition.set = noop  } else {    // ...  }  // ...  Object.defineProperty(target, key, sharedPropertyDefinition)}function createComputedGetter (key) {  return function computedGetter () {    const watcher = this._computedWatchers && this._computedWatchers[key]    if (watcher) {      if (watcher.dirty) {        watcher.evaluate()      }      if (Dep.target) {        watcher.depend()      }      return watcher.value    }  }}

        能够看到创立Watcher实例时传入一个配置项{ lazy: true },再看Watcher的结构器中的代码,即默认watcher.dirtytrue,所以执行watcher.evaluate()watcher.get()

        watcher.get()会去执行计算方法或者计算属性的get()办法,即this.getter.call(vm, vm)

        // src/core/observer/watcher.jsconstructor (    vm: Component,    expOrFn: string | Function,    cb: Function,    options?: ?Object,    isRenderWatcher?: boolean  ) {    this.vm = vm    if (isRenderWatcher) {      vm._watcher = this    }    vm._watchers.push(this)    // options    if (options) {      // ...      this.lazy = !!options.lazy      // ...    } else {      // ...    }    // ...    this.dirty = this.lazy // for lazy watchers    // ...}evaluate () {    this.value = this.get()    this.dirty = false}get() {    pushTarget(this)    let value    const vm = this.vm    try {      value = this.getter.call(vm, vm)    } catch (e: any) {      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) {        traverse(value)      }      popTarget()      this.cleanupDeps()    }    return value}depend() {    let i = this.deps.length    while (i--) {      this.deps[i].depend()    }}
      5. initWatch:解决自定义监听$options.watch

        执行了$watch办法,能够先看下它的定义:

        // src/core/instance/state.jsVue.prototype.$watch = function (    expOrFn: string | (() => any),    cb: any,    options?: Record<string, any>  ): Function {    const vm: Component = this    if (isPlainObject(cb)) {      return createWatcher(vm, expOrFn, cb, options)    }    options = options || {}    options.user = true    const watcher = new Watcher(vm, expOrFn, cb, options)    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()    } }

        能够看到也是创立了一个Watcher实例对象。

    3. initProvide:解决$options.provide,将provide的数据(或者provide执行后的数据)挂在实例的_provided属性上
    4. 触发created钩子
  5. 最初执行vm.$mount办法,执行挂载流程,因为挂载的形式由平台决定,所以$mount的办法并未定义在src/core中;web端的$mount办法定义在src/platforms/web/runtime/index.js中。

    // src/platforms/web/runtime/index.jsVue.prototype.$mount = function (  el?: string | Element,  hydrating?: boolean): Component {  el = el && inBrowser ? query(el) : undefined  return mountComponent(this, el, hydrating)}

    调用的mountComponent(this, el, hydrating)定义在src/core/instance/lifecycle.js中。

    // src/core/instance/lifecycle.jsexport function mountComponent (  vm: Component,  el: ?Element,  hydrating?: boolean): Component {  vm.$el = el  if (!vm.$options.render) {    vm.$options.render = createEmptyVNode    if (process.env.NODE_ENV !== 'production') {      /* istanbul ignore if */      if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||        vm.$options.el || el) {        warn(          'You are using the runtime-only build of Vue where the template ' +          'compiler is not available. Either pre-compile the templates into ' +          'render functions, or use the compiler-included build.',          vm        )      } else {        warn(          'Failed to mount component: template or render function not defined.',          vm        )      }    }  }  callHook(vm, 'beforeMount')  let updateComponent  /* istanbul ignore if */  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {    updateComponent = () => {      const name = vm._name      const id = vm._uid      const startTag = `vue-perf-start:${id}`      const endTag = `vue-perf-end:${id}`      mark(startTag)      const vnode = vm._render()      mark(endTag)      measure(`vue ${name} render`, startTag, endTag)      mark(startTag)      vm._update(vnode, hydrating)      mark(endTag)      measure(`vue ${name} patch`, startTag, endTag)    }  } else {    updateComponent = () => {      vm._update(vm._render(), hydrating)    }  }  // we set this to vm._watcher inside the watcher's constructor  // since the watcher's initial patch may call $forceUpdate (e.g. inside child  // component's mounted hook), which relies on vm._watcher being already defined  new Watcher(vm, updateComponent, noop, {    before () {      if (vm._isMounted && !vm._isDestroyed) {        callHook(vm, 'beforeUpdate')      }    }  }, true /* isRenderWatcher */)  hydrating = false  // manually mounted instance, call mounted on self  // mounted is called for render-created child components in its inserted hook  if (vm.$vnode == null) {    vm._isMounted = true    callHook(vm, 'mounted')  }  return vm}

    见名知意,是对挂载的解决:

    1. 拿到el放在vm.$el上
    2. 确认是否有vm.$options.render,没有则赋值创立一个空的VNode实例的办法
    3. 触发beforeMount钩子
    4. 创立一个新的Watcher实例,用于实例更新后触发从新渲染

      updateComponent = () => {  vm._update(vm._render(), hydrating)}

      并传递一个before办法,用于在组件更新前触发beforeUpdate钩子

    5. 触发mounted钩子

Vue利用初始化大抵就是这样一个流程