前言
首先:我们引用官方的案例,先来认识一下 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,则初始化创建监听)