zoukankan      html  css  js  c++  java
  • vue2源码解析(三)

    研究组件的注册
    //组件注册
    // ASSET_TYPES = ['component','filter','directive'] ASSET_TYPES.forEach(type => { // 声明静态的方法: Vue.component = function(){} // 平时声明的组件是Vue.component('comp',{}) //id ='comp' ,definition = {} 配置 Vue[type] = function ( id: string, definition: Function | Object ): Function | Object | void { if (!definition) { return this.options[type + 's'][id] } else { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && type === 'component') { validateComponentName(id) } if (type === 'component' && isPlainObject(definition)) { // name的设置 definition.name = definition.name || id // this.options._base指的是vue的构造函数,实际上是Vue.extend({}),Vue.extend({})返回是是Ctor组件的构造函数 // 当用Vue.extend({})组件的构造函数,最后可以用new Ctor()去实例化组件的构造函数 // 将传入的组件配置对象转换为组件构造函数 definition = this.options._base.extend(definition) } if (type === 'directive' && typeof definition === 'function') { definition = { bind: definition, update: definition } } // 向全局的选项中加入全局组件配置对象 // 每个组件上components的id [id]添加构造函数 Ctor,即 // components[id] = Ctor // Vue.component
         // 到时候mergeOptions()的时候再把this.options merge进去,这样就变成了全局组件,其他的全局组件还包括keep-alive,transition等 this.options[type + 's'][id] = definition return definition } } }) }  

    全局组件注册完了,接下来生命周期就开始了,怎么去实例化Ctor,平时大家看的时候,只看过怎么去new Vue的,从来没看过怎么去实例化组件的。

    new Vue的时候,首次会执行render(),由于在执行根实例的render的时候,可以得到整棵树的虚拟dom的结构

    所以接下来从initRender入手 srccoreinstance ender.js

    export function initRender (vm: Component) {
      vm._vnode = null // the root of the child tree
      vm._staticTrees = null // v-once cached trees
      const options = vm.$options
      const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
      const renderContext = parentVnode && parentVnode.context
      vm.$slots = resolveSlots(options._renderChildren, renderContext)
      vm.$scopedSlots = emptyObject
      // bind the createElement fn to this instance
      // so that we get proper render context inside it.
      // args order: tag, data, children, normalizationType, alwaysNormalize
      // internal version is used by render functions compiled from templates
      // 由编译器生成的渲染函数调用_c
      vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
      // normalization is always applied for the public version, used in
      // user-written render functions.
      // 其实是render函数中的参数h
      vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
    
      // $attrs & $listeners are exposed for easier HOC creation.
      // they need to be reactive so that HOCs using them are always updated
      const parentData = parentVnode && parentVnode.data
    
      /* istanbul ignore else */
      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 {
        // 定义$attrs和$listeners响应式
        defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
        defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
      }
    }  

    接下来看createElement  srccorevdomcreate-element.js

    createElement的作用是将传入的组件配置转化为vnode

    // 核心逻辑(生成虚拟dom)
      let vnode, ns
      if (typeof tag === 'string') {
        // 拿到组件的构造函数 Ctor
        let Ctor
        ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
        // 判断是否是保留标签 div p span
        if (config.isReservedTag(tag)) {
          // platform built-in elements
          if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.nativeOn)) {
            warn(
              `The .native modifier for v-on is only valid on components but it was used on <${tag}>.`,
              context
            )
          }
          vnode = new VNode(
            config.parsePlatformTagName(tag), data, children,
            undefined, undefined, context
          )
        // isDef(Ctor)有没有定义一个Ctor构造函数
        } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
          // component
          // Ctor 这里就是组件构造函数 得到vnode
        这里就用到了createComponent
    vnode = createComponent(Ctor, data, context, children, tag) } else { // unknown or unlisted namespaced elements // check at runtime because it may get assigned a namespace when its // parent normalizes children vnode = new VNode( tag, data, children, undefined, undefined, context ) } } else { // direct component options / constructor vnode = createComponent(tag, data, context, children) }

    进入到createComponent里面

    // 安装组件的管理钩子
      // 管理钩子:合并用户编写的钩子喝系统默认的钩子
      installComponentHooks(data)  => hooksToMerge就是系统默认的钩子    hooksToMerge = Object.keys(componentVNodeHooks)
      // return a placeholder vnode
      // 把组件构造函数的名称name拿出来,如果没有,就用tag(它自己的名称)
      const name = Ctor.options.name || tag
      // vue-component-1-comp
      const vnode = new VNode(
        `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
        data, undefined, undefined, undefined, context,
        { Ctor, propsData, listeners, tag, children },
        asyncFactory
      )

    接下来具体看componentVNodeHooks

     最后组件实例要进行挂载

    父子组件生命周期 创建的时候自上而下,挂载的时候自下而上(洋葱模型)

    上面的init是什么时候执行的?
    答案:会在patch的时候执行

    parent.$mount  =>  render()方法   =>  update()函数  =>  _patch_()  => _patch_方法中有createElement()方法,init就是在这个时候被调用的

  • 相关阅读:
    ACM 一种排序
    ACM Binary String Matching
    ACM 括号配对问题
    ACM BUYING FEED
    ACM 喷水装置(二)
    ACM 会场安排问题
    ACM 推桌子
    ACM 心急的C小加
    ACM 田忌赛马
    ACM 疯牛
  • 原文地址:https://www.cnblogs.com/gengzhen/p/15440205.html
Copyright © 2011-2022 走看看