美文网首页程序员
vue 组件原理及创建过程,

vue 组件原理及创建过程,

作者: 臣以君纲 | 来源:发表于2019-01-21 23:43 被阅读1次

    之前我们提到,当vue再挂载到真实dom元素上时,会执行所挂载vue对象的render函数,render函数主要内容就是执行 $creatElement返回一个vdom,而如果我们碰到的render函数的tag并不是一个普通标签,而是一个组件时,会执行createComponent 方法,创建一个组件vdom,这里面的逻辑是和其他vdom的创建非常不一样的,,我们进入代码

    export function createComponent (
      Ctor: Class<Component> | Function | Object | void,
      data: ?VNodeData,
      context: Component,
      children: ?Array<VNode>,
      tag?: string
    ): VNode | Array<VNode> | void {
      if (isUndef(Ctor)) {
        return
      }
    
      const baseCtor = context.$options._base
    
      // plain options object: turn it into a constructor
      if (isObject(Ctor)) {
        Ctor = baseCtor.extend(Ctor)
      }
    
      // if at this stage it's not a constructor or an async component factory,
      // reject.
      if (typeof Ctor !== 'function') {
        if (process.env.NODE_ENV !== 'production') {
          warn(`Invalid Component definition: ${String(Ctor)}`, context)
        }
        return
      }
    
      // async component
      let asyncFactory
      if (isUndef(Ctor.cid)) {
        asyncFactory = Ctor
        Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
        if (Ctor === undefined) {
          // return a placeholder node for async component, which is rendered
          // as a comment node but preserves all the raw information for the node.
          // the information will be used for async server-rendering and hydration.
          return createAsyncPlaceholder(
            asyncFactory,
            data,
            context,
            children,
            tag
          )
        }
      }
    
      data = data || {}
    
      // resolve constructor options in case global mixins are applied after
      // component constructor creation
      resolveConstructorOptions(Ctor)
    
      // transform component v-model data into props & events
      if (isDef(data.model)) {
        transformModel(Ctor.options, data)
      }
    
      // extract props
      const propsData = extractPropsFromVNodeData(data, Ctor, tag)
    
      // functional component
      if (isTrue(Ctor.options.functional)) {
        return createFunctionalComponent(Ctor, propsData, data, context, children)
      }
    
      // extract listeners, since these needs to be treated as
      // child component listeners instead of DOM listeners
      const listeners = data.on
      // replace with listeners with .native modifier
      // so it gets processed during parent component patch.
      data.on = data.nativeOn
    
      if (isTrue(Ctor.options.abstract)) {
        // abstract components do not keep anything
        // other than props & listeners & slot
    
        // work around flow
        const slot = data.slot
        data = {}
        if (slot) {
          data.slot = slot
        }
      }
    
      // install component management hooks onto the placeholder node
      installComponentHooks(data)
    
      // return a placeholder vnode
      const name = Ctor.options.name || tag
      const vnode = new VNode(
        `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
        data, undefined, undefined, undefined, context,
        { Ctor, propsData, listeners, tag, children },
        asyncFactory
      )
    
      // Weex specific: invoke recycle-list optimized @render function for
      // extracting cell-slot template.
      // https://github.com/Hanks10100/weex-native-directive/tree/master/component
      /* istanbul ignore if */
      if (__WEEX__ && isRecyclableComponent(vnode)) {
        return renderRecyclableComponentTemplate(vnode)
      }
    
      return vnode
    }
    

    根据flow的定义我们可以看到这个函数会返回一个vdom或者vdom数组或者空,函数中有一个定义const baseCtor = context.$options._base,这个其实就是vm本身,然后执行 extend方法,这也是在Vue方法被定义时被同时初始化的方法,我们进入extend

      Vue.extend = function (extendOptions: Object): Function {
        extendOptions = extendOptions || {}
        const Super = this
        const SuperId = Super.cid
        const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
        if (cachedCtors[SuperId]) {
          return cachedCtors[SuperId]
        }
    
        const name = extendOptions.name || Super.options.name
        if (process.env.NODE_ENV !== 'production' && name) {
          validateComponentName(name)
        }
    
        const Sub = function VueComponent (options) {
          this._init(options)
        }
        Sub.prototype = Object.create(Super.prototype)
        Sub.prototype.constructor = Sub
        Sub.cid = cid++
        Sub.options = mergeOptions(
          Super.options,
          extendOptions
        )
        Sub['super'] = Super
    
        // For props and computed properties, we define the proxy getters on
        // the Vue instances at extension time, on the extended prototype. This
        // avoids Object.defineProperty calls for each instance created.
        if (Sub.options.props) {
          initProps(Sub)
        }
        if (Sub.options.computed) {
          initComputed(Sub)
        }
    
        // allow further extension/mixin/plugin usage
        Sub.extend = Super.extend
        Sub.mixin = Super.mixin
        Sub.use = Super.use
    
        // create asset registers, so extended classes
        // can have their private assets too.
        ASSET_TYPES.forEach(function (type) {
          Sub[type] = Super[type]
        })
        // enable recursive self-lookup
        if (name) {
          Sub.options.components[name] = Sub
        }
    
        // keep a reference to the super options at extension time.
        // later at instantiation we can check if Super's options have
        // been updated.
        Sub.superOptions = Super.options
        Sub.extendOptions = extendOptions
        Sub.sealedOptions = extend({}, Sub.options)
    
        // cache constructor
        cachedCtors[SuperId] = Sub
        return Sub
      }
    }
    

    首先这里判断了一下缓存,如果已有此组件的缓存,直接返回,否则的话,将会返回一个继承了Vue的子组件构造器函数,Sub.prototype=object.create(super.prototype)Sub ,
    重新回到createComponent中,中间有一些异步组件的逻辑判断,对v-model的判断,对hooks的添加,最后返回一个component 类型的vnode,然后重新回到 之前提到的_update,进入里面的patch方法,,patch中对组件的vnode也有不同的处理,会判断vnode 是否时component类型,如果是的话,经过一些步骤,最后在init hooks中,,会执行子组件构造器的init(继承于vue)方法,init 方法重新回到最初始的init方法,

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

    这里注意一下initlifecycle,这里会保存一下当前组件的父组件,从而能够构造出父子关系,然后这里因为子组件的option没有el,$mount 不会在这里执行,会在上一个函数执行子构造函数的mount方法,然后又回到之前的mountComponent ,new Watcher(),updateComponent,然后执行子组件的render函数,update,子组件的patch,creatElem,注意,这里不会执行insert方法,真正的执行真实dom树插入,而是停在dom树创建完毕的情况,当然,如果在creatElem 过程中如果又碰到组件还会执行这一整个组件的处理流程,通过这样一个深度优先遍历 整个嵌套组件的dom tree便会完美额构建完成,最后一次性插入到真实dom中

    相关文章

      网友评论

        本文标题:vue 组件原理及创建过程,

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