VueJs 的组件化

285 阅读2分钟

概述

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子类。它们会在适当的时机里实例化并挂载到父组件。

组件的实例化过程

在根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