vue源码理解——全局API篇

全局API

与实例方法不同,实例方法是将方法挂载到Vue的原型上,而全局API是直接在Vue上挂载方法。在Vue中,全局API一共有12个,分别是Vue.extendVue.nextTickVue.setVue.deleteVue.directiveVue.filterVue.componentVue.useVue.mixinVue.observableVue.version。这12个API中有的是我们在日常业务开发中经常会用到的,有的是对Vue内部或外部插件提供的,我们在日常业务开发中几乎用不到。接下来我们就对这12个API逐个进行分析,看看其内部原理都是怎样的。

Vue.extend

用法回顾

其用法如下:

Vue.extend( options )
  • 参数

    • {Object} 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')

 结果如下:

<p>Walter White aka Heisenberg</p>

原理分析

通过用法回顾我们知道,Vue.extend的作用是创建一个继承自Vue类的子类,可接收的参数是一个包含组件选项的对象。

既然是Vue类的子类,那么除了它本身独有的一些属性方法,还有一些是从Vue类中继承而来,所以创建子类的过程其实就是一边给子类上添加上独有的属性,一边将父类的公共属性复制到子类上。接下来,我们就来看看源码是如何实现这个过程的。

该API的定义位于源码的src/core/global-api/extend.js中,如下:

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

    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
    }

    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)

    // cache constructor
    cachedCtors[SuperId] = Sub
    return Sub
}

可以看到,虽然代码量稍微有点多,但是逻辑并不复杂,下面我们就来逐行分析一下。

首先,该函数内部定义了几个变量,如下:

extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
  • extendOptions:用户传入的一个包含组件选项的对象参数;
  • Super:指向父类,即基础 Vue类;
  • SuperId:父类的cid属性,无论是基础 Vue类还是从基础 Vue类继承而来的类,都有一个cid属性,作为该类的唯一标识;
  • cachedCtors:缓存池,用于缓存创建出来的类;

接着,在缓存池中先尝试获取是否之前已经创建过的该子类,如果之前创建过,则直接返回之前创建的。之所以有这一步,是因为Vue为了性能考虑,反复调用Vue.extend其实应该返回同一个结果,只要返回结果是固定的,就可以将结果缓存,再次调用时,只需从缓存中取出结果即可。在API方法定义的最后,当创建完子类后,会使用父类的cid作为key,创建好的子类作为value,存入缓存池cachedCtors中。如下:

if (cachedCtors[SuperId]) {
    return cachedCtors[SuperId]
}

接着,获取到传入的选项参数中的name字段,并且在开发环境下校验name字段是否合法,如下:

const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
    validateComponentName(name)
}

接着,创建一个类Sub,这个类就是将要继承基础Vue类的子类,如下:

const Sub = function VueComponent (options) {
    this._init(options)
}

到这里,我们已经把类创建好了,接下来的工作就是让该类去继承基础Vue类,让其具备一些基础Vue类的能力。

首先,将父类的原型继承到子类中,并且为子类添加唯一标识cid,如下:

Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++

接着,将父类的options与子类的options进行合并,将合并结果赋给子类的options属性,如下:

接着,将父类保存到子类的super属性中,以确保在子类中能够拿到父类,如下:

Sub['super'] = Super

接着,如果选项中存在props属性,则初始化它,如下:

if (Sub.options.props) {
    initProps(Sub)
}

function initProps (Comp) {
  const props = Comp.options.props
  for (const key in props) {
    proxy(Comp.prototype, `_props`, key)
  }
}

初始化props属性其实就是把参数中传入的props选项代理到原型的_props中。

接着,如果选项中存在computed属性,则初始化它,如下:

if (Sub.options.computed) {
    initComputed(Sub)
}

function initComputed (Comp) {
  const computed = Comp.options.computed
  for (const key in computed) {
    defineComputed(Comp.prototype, key, computed[key])
  }
}

初始化props属性就是遍历参数中传入的computed选项,将每一项都调用defineComputed函数定义到子类原型上。此处的defineComputed函数与我们之前在生命周期初始化阶段initState中所介绍的defineComputed函数是一样的。

接着,将父类中的一些属性复制到子类中,如下:

Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use

export const ASSET_TYPES = [
  'component',
  'directive',
  'filter'
]
// 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
}

接着,给子类新增三个独有的属性,如下:

Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)

最后,使用父类的cid作为key,创建好的子类Sub作为value,存入缓存池cachedCtors中。如下:

// cache constructor
cachedCtors[SuperId] = Sub

最终将创建好的子类Sub返回。

以上,就是Vue.extend的所有逻辑。其实总体来讲,整个过程就是先创建一个类Sub,接着通过原型继承的方式将该类继承基础Vue类,然后给Sub类添加一些属性以及将父类的某些属性复制到Sub类上,最后将Sub类返回。

撸码一时爽,一直撸码一直爽!!!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值