vue训练营5 --- 谈一谈对vue组件化的理解

源码分析1:组件定义  

src\core\global-api\assets.js

 

源码分析2:组件化优点  

src\core\instance\lifecycle.jslifecycle.js - mountComponent()

组件、Watcher、渲染函数和更新函数之间的关系

 

源码分析3:组件化实现  

构造函数,src\core\global-api\extend.js
实例化及挂载,src\core\vdom\patch.js - createElm()

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

源码分析1:组件定义  

src\core\global-api\assets.js

export function initAssetRegisters (Vue: GlobalAPI) {

  /**

   * Create asset registration methods.

   */

  // ['component', 'filter', 'directive']

  ASSET_TYPES.forEach(type => {

    // Vue['component'] = function(id,definition){}

    Vue[type] = function (

      id: string,

      definition: Function | Object

    ): Function | Object | void {

      if (!definition) {

        return this.options[type + 's'][id]

      } else {

        /* istanbul ignore if */

        // Vue.component('comp', {data(){}})

        if (process.env.NODE_ENV !== 'production' && type === 'component') {

          validateComponentName(id)

        }

        // definition是对象, 是自定义组件配置选项

        if (type === 'component' && isPlainObject(definition)) {

          // 定义组件 name

          definition.name = definition.name || id

          // extend 创建组件构造函数,def变成了构造函数

          definition = this.options._base.extend(definition)

        }

        if (type === 'directive' && typeof definition === 'function') {

          definition = { bind: definition, update: definition }

        }

        // 注册 this.options[components][comp] = Ctor

        this.options[type + 's'][id] = definition

        return definition

      }

    }

  })

}

 

源码分析2:组件化优点  

src\core\instance\lifecycle.jslifecycle.js - mountComponent()

组件、Watcher、渲染函数和更新函数之间的关系

export 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 {

    // 用户 $mount()时,定义 updateComponent

    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

 

  /*

      一个组件创建一次 Watcher, Wactcher的实例和组件的实例一一对应

      但是一个组件可能存在多个 data 中的key的使用,

      在更新时,为了确保知道是哪个 key 发生了变化,只能采用 diff 算法

      这也是 diff 存在的必要性

  */

  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

}

 

源码分析3:组件化实现  

构造函数,src\core\global-api\extend.js

 

// extendOptions: Object  自定义组件配置选项

  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)

    }

 

    // 创建一个 VueComponent 类

    const Sub = function VueComponent (options) {

      this._init(options)

    }

    // 继承于 Vue

    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

  }


实例化及挂载,src\core\vdom\patch.js - createElm()

function createElm (

    vnode,

    insertedVnodeQueue,

    parentElm,

    refElm,

    nested,

    ownerArray,

    index

  ) {

    if (isDef(vnode.elm) && isDef(ownerArray)) {

      // This vnode was used in a previous render!

      // now it's used as a new node, overwriting its elm would cause

      // potential patch errors down the road when it's used as an insertion

      // reference node. Instead, we clone the node on-demand before creating

      // associated DOM element for it.

      vnode = ownerArray[index] = cloneVNode(vnode)

    }

 

    vnode.isRootInsert = !nested // for transition enter check

    // 如果要创建的是组件,走下面的流程

    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {

      return

    }

 

    // 原生标签创建

    const data = vnode.data

    const children = vnode.children

    const tag = vnode.tag

    if (isDef(tag)) {

      if (process.env.NODE_ENV !== 'production') {

        if (data && data.pre) {

          creatingElmInVPre++

        }

        if (isUnknownElement(vnode, creatingElmInVPre)) {

          warn(

            'Unknown custom element: <' + tag + '> - did you ' +

            'register the component correctly? For recursive components, ' +

            'make sure to provide the "name" option.',

            vnode.context

          )

        }

      }

 

      vnode.elm = vnode.ns

        ? nodeOps.createElementNS(vnode.ns, tag)

        : nodeOps.createElement(tag, vnode)

      setScope(vnode)

 

      /* istanbul ignore if */

      if (__WEEX__) {

        // in Weex, the default insertion order is parent-first.

        // List items can be optimized to use children-first insertion

        // with append="tree".

        const appendAsTree = isDef(data) && isTrue(data.appendAsTree)

        if (!appendAsTree) {

          if (isDef(data)) {

            invokeCreateHooks(vnode, insertedVnodeQueue)

          }

          insert(parentElm, vnode.elm, refElm)

        }

        createChildren(vnode, children, insertedVnodeQueue)

        if (appendAsTree) {

          if (isDef(data)) {

            invokeCreateHooks(vnode, insertedVnodeQueue)

          }

          insert(parentElm, vnode.elm, refElm)

        }

      } else {

        createChildren(vnode, children, insertedVnodeQueue)

        if (isDef(data)) {

          invokeCreateHooks(vnode, insertedVnodeQueue)

        }

        insert(parentElm, vnode.elm, refElm)

      }

 

      if (process.env.NODE_ENV !== 'production' && data && data.pre) {

        creatingElmInVPre--

      }

    } else if (isTrue(vnode.isComment)) {

      vnode.elm = nodeOps.createComment(vnode.text)

      insert(parentElm, vnode.elm, refElm)

    } else {

      vnode.elm = nodeOps.createTextNode(vnode.text)

      insert(parentElm, vnode.elm, refElm)

    }

  }

 

  // 这里 createComponent 是把前面的那个执行的结果vnode转换为这是dom

  function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {

    // 获取管理钩子函数

    let i = vnode.data

    if (isDef(i)) {

      const isReactivated = isDef(vnode.componentInstance) && i.keepAlive

      // 存在 init 钩子,则执行创建实例并挂载

      if (isDef(i = i.hook) && isDef(i = i.init)) {

        i(vnode, false /* hydrating */)

      }

      // after calling the init hook, if the vnode is a child component

      // it should've created a child instance and mounted it. the child

      // component also has set the placeholder vnode's elm.

      // in that case we can just return the element and be done.

 

      // 如果组件实例存在

      if (isDef(vnode.componentInstance)) {

        // 属性初始化

        initComponent(vnode, insertedVnodeQueue)

        // dom 插入操作

        insert(parentElm, vnode.elm, refElm)

        if (isTrue(isReactivated)) {

          reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)

        }

        return true

      }

    }

  }

 

组件定义:

Vue.component('comp', {

    template: '<div>this is a component</div>' })

 

<template>

    <div>

        this is a component     </div>

</template>

vue-loader会编译templaterender函数,最终导出的依然是组件配置对象。

 

结论:

1. 组件是独立和可复用的代码组织单元。组件系统是 Vue 核心特性之一,它使开发者使用小型、独
立和通常可复用的组件构建大型应用;

2. 组件化开发能大幅提高应用开发效率、测试性、复用性等;

3.组件使用按分类有:页面组件、业务组件、通用组件;

4. vue的组件是基于配置的,我们通常编写的组件是组件配置而非组件,框架后续会生成其构造函

数,它们基于VueComponent,扩展于Vue

5. vue中常见组件化技术有:属性prop,自定义事件,插槽等,它们主要用于组件通信、扩展等;

6. 合理的划分组件,有助于提升应用性能;
7. 组件应该是高内聚、低耦合的;

8. 遵循单向数据流的原则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值