Skip to content

VueJs 的组件化 #93

@coconilu

Description

@coconilu

概述

Vue支持封装HTML元素为组件,组件还可以组装成另一个组件,提高可复用性。

那么,有没有想过这些是怎么办到的呢?

简单地拆分一小块HTML代码?不是的,前面提到过,Vue是通过render函数生成DOM的,所以组件也应该是由一个JavaScript对象表示,并且该对象会包含一个render函数。

其实除了根Vue实例,其它的组件都是Vue的子类的实例。

构造Vue子类的方法

Vue的子类是通过API: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-cli开发的使用,每一个.vue文件会被解析转化为一个普通对象,该对象的render属性是通过template转化来的。然后这个普通对象会被传入Vue.extend(),生成一个Vue的子类。

也就是说,从根Vue类开始,components属性会连接很多Vue子类,然后Vue子类的components也会连接其它的Vue子类。它们会在适当的时机里实例化并挂载到父组件。

组件的实例化过程

在完成父组件的整个 patch 过程后,最后执行 insert(parentElm, vnode.elm, refElm) 完成组件的 DOM 插入,如果组件 patch 过程中又创建了子组件,那么DOM 的插入顺序是先子后父。

在根Vue实例或者组件的render函数执行过程中,如果遇到另一个组件,就会使用内部的createComponent处理,大概分为三个步骤:

  1. 使用Vue.extend()创建Vue子类
  2. 把组件的钩子函数(包括init、prepatch、insert、destroy)注册到snabbdom的patch过程中
  3. 返回组件vnode(children属性为undefined)

在根Vue实例或者组件的patch函数执行过程中,调用组件的初始化钩子函数——内部会调用createComponentInstanceForVnode返回组件实例,并在恰当时机挂载组件到父组件。

组件的生命周期

与根Vue实例一样,组件也是有创建-挂载-更新三个阶段。

且各个组件(包括父子组件、兄弟组件)的更新都是独立的,也就是说,一个组件的options.data中的数据更新并触发组件render重新执行的时候,并不会导致其它的组件更新。

参考

createComponent
patch
Vue.js 技术揭秘——patch

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions