美文网首页
vue源码分析(五)new Vue()之后发生了什么(第一篇)

vue源码分析(五)new Vue()之后发生了什么(第一篇)

作者: vue爱好者 | 来源:发表于2020-04-21 20:48 被阅读0次

    前几章我们说了new Vue()之前做了哪些操作,今天我们就来看看new vue()里面做了哪些有趣的操作,我们之前说过 “src/core/instance/index.js” 文件里面定义了一个vue函数,并且调用了“_init”方法。

    代码如下:

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

    我们看到这里有一个if判断 “instanceof”,确保你是通过new 关键字来调用vue函数的,紧接着就是调用“_init”函数。

    _init代码如下:

    export function initMixin (Vue: Class<Component>) {
      Vue.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)
        }
      }
    }
    

    首先声明了一个vm变量并且把this赋值给了vm,紧接着给vm添加了两个标识“_uid”、“_isVue”,接下来看是否有“_isComponent”参数,有的话,会做一些组件内部选项的特殊处理,否则的话就调用“mergeOptions”函数,处理传递过来的参数,主要是给vue挂载了directives、filters、components这三个属性。

    然后调用了一个“initProxy”函数,对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')
    

    initLifecycle(vm)

    这个函数主要是做一些生命周期的初始化标识工作。
    代码如下:

    export function initLifecycle (vm: Component) {
      const options = vm.$options
    
      // locate first non-abstract parent
      let parent = options.parent
      if (parent && !options.abstract) {
        while (parent.$options.abstract && parent.$parent) {
          parent = parent.$parent
        }
        parent.$children.push(vm)
      }
    
      vm.$parent = parent
      vm.$root = parent ? parent.$root : vm
    
      vm.$children = []
      vm.$refs = {}
    
      vm._watcher = null
      vm._inactive = null
      vm._directInactive = false
      vm._isMounted = false
      vm._isDestroyed = false
      vm._isBeingDestroyed = false
    }
    

    initEvents(vm)

    这个函数就对vue赋值了“_events”、“_hasHookEvent”属性,代码如下:

    export function initEvents (vm: Component) {
      vm._events = Object.create(null)
      vm._hasHookEvent = false
      // init parent attached events
      const listeners = vm.$options._parentListeners
      if (listeners) {
        updateComponentListeners(vm, listeners)
      }
    }
    

    initRender(vm)

    这个函数主要是对渲染做一个初始化的挂载处理,使用“defineReactive”函数对“$attrs”、“$listeners”属性进行一个拦截操作。至于“defineReactive”函数的具体代码可以看 vue源码分析(六)核心函数之defineReactive

     if (process.env.NODE_ENV !== 'production') {
        defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
          !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
        }, true)
        defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
          !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
        }, true)
      } else {
        defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
        defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
      }
    

    callHook(vm, 'beforeCreate')

    这是函数主要是用来触发生命周期函数。代码如下:

    export function callHook (vm: Component, hook: string) {
      // #7573 disable dep collection when invoking lifecycle hooks
      pushTarget()
      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()
    }
    

    首先会有一个入栈操作 “pushTarget()”,然后再触发你传递进来的生命周期函数,可以看到这里是执行了“beforeCreate”生命周期函数,至于_hasHookEvent默认就是fasle,最后再有一个出栈操作“popTarget()”。

    initInjections(vm)

    这是函数主要是用来处理“inject”选项的,至于inject的用法大家可以参考vue的官方文档,下面引用官方的一句话。

    provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。

    initState(vm)

    这是函数主要是用来对“props”、"methods"、“data”等参数的处理。代码如下:

    export function initState (vm: Component) {
      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)
      }
    }
    

    至于里面调用的函数 initPropsinitMethodsinitDataobserveinitComputedinitWatch、方法的具体分析,请点击进行查看。

    initProvide(vm)

    这个函数就简单,没啥好说的,看代码吧:

    export function initProvide (vm: Component) {
      const provide = vm.$options.provide
      if (provide) {
        vm._provided = typeof provide === 'function'
          ? provide.call(vm)
          : provide
      }
    }
    

    callHook(vm, 'created')

    接下来调了“callHook”函数触发了,“created”生命周期函数。

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

    最后触发了“$mount”函数。至于 mount 里面发生了什么,请点击查看。

    相关文章

      网友评论

          本文标题:vue源码分析(五)new Vue()之后发生了什么(第一篇)

          本文链接:https://www.haomeiwen.com/subject/qijkyhtx.html