Bootstrap

Vue原理 【 Vue.extends 】

前言

首先:我们引用官方的案例,先来认识一下 vue.extends Api 的介绍和用法

Vue.extends(options)

使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象

data 选项是特例,需要注意 - 在 Vue.extend() 中它必须是函数

<div id="mount-point"></div>
// 创建构造器
var Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})

// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount(’#mount-point’)

结果如下:注意:这里是替换 id为"mount-point"的div替换为如下

<p>Walter White aka Heisenberg</p>

好,通过源码来看一下它是如何实现的

extends 原理
/* @flow */

import { ASSET_TYPES } from 'shared/constants'
import { defineComputed, proxy } from '../instance/state'
import { extend, mergeOptions, validateComponentName } from '../util/index'

export function initExtend (Vue: GlobalAPI) {
 // 每一个组件实例都有一个唯一的 cid 
  Vue.cid = 0
  let cid = 1

  // Vue.extend 方法实体
  Vue.extend = function (extendOptions: Object): Function {
    // 用户传进来的参数,包含组件实例的对象
    // {template:'',data:function (){},methods:{}....} 等
    extendOptions = extendOptions || {}
    // 将 Super 指向父类 this ,也就是 Vue
    const Super = this
    // SuperId 使用父类的唯一cid
    const SuperId = Super.cid
    // 创建缓存,组成 {cachedCtors[SuperId]:sub ...} 的形式
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }
    // 组件实例 name 
    const name = extendOptions.name || Super.options.name
    if (process.env.NODE_ENV !== 'production' && name) {
      validateComponentName(name)
    }
    // 下边就是原型链继承,创建子类构造函数,初始化options
    const Sub = function VueComponent (options) {
      this._init(options)
    }
    // 通过create继承父类原型
    Sub.prototype = Object.create(Super.prototype)
    // 修改子类 constructor 指向自己
    Sub.prototype.constructor = Sub
    // 创建子类唯一 cid
    Sub.cid = cid++
    // 合并父类options和子类自有options
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    // 定义子类与父类的关系
    Sub['super'] = Super

    // 子类有props,初始化子类props,并创建监听
    if (Sub.options.props) {
      initProps(Sub)
    }
    // 子类有computed,初始化computed,并创建监听
    if (Sub.options.computed) {
      initComputed(Sub)
    }

    // 将父类的一些属性复制给子类
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })

    if (name) {
      Sub.options.components[name] = Sub
    }
    
    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)
	// 实现上边说的缓存
    cachedCtors[SuperId] = Sub
    // 返回子类 , 实现官方案例那样,new一个实例,通过 $mount 挂载 。
    return Sub
  }
}

// 初始化 props
function initProps (Comp) {
  const props = Comp.options.props
  for (const key in props) {
    proxy(Comp.prototype, `_props`, key)
  }
}
// 初始化 computed ,用的就是vue本身计算属性的 defineComputed方式,我们后章再分析 
function initComputed (Comp) {
  const computed = Comp.options.computed
  for (const key in computed) {
    defineComputed(Comp.prototype, key, computed[key])
  }
}

总结

通过看源码的整个过程,可以看出来,正如官方案例所讲:使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象

就是基于 Vue 构造函数,创建一个子类,然后继承父类的参数和方法,最后再返回这个子类(子类包含props和computed,则初始化创建监听)

;