Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 源码学习(三)- Vue.extend #10

Open
zhuping opened this issue Apr 9, 2019 · 0 comments
Open

Vue 源码学习(三)- Vue.extend #10

zhuping opened this issue Apr 9, 2019 · 0 comments

Comments

@zhuping
Copy link
Owner

zhuping commented Apr 9, 2019

在上一篇构造函数部分,我们知道了在 global-api 添加全局 API 的时候,在 Vue 上增加了 Vue.extend 方法,因为考虑到后面讲解 initMixin 部分有涉及到这部分的内容,所以今天先来讲讲这个 Vue.extend 好了。

通过 Vue 的 extend 文档 我们知道,它是使用基础 Vue 构造器,可以用来创建一个“子类”。比如:

<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'
    }
  }
})

new Profile().$mount('#mount-point')

结果如下:

<p>Walter White aka Heisenberg</p>

我们直接来看下代码:

export function initExtend (Vue: GlobalAPI) {
  /**
   * Each instance constructor, including Vue, has a unique
   * cid. This enables us to create wrapped "child
   * constructors" for prototypal inheritance and cache them.
   */
  Vue.cid = 0
  let cid = 1

  /**
   * Class inheritance
   */
  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
  }
}

下面我们来逐行分析下代码,extendOptions 为通过 Vue.extend 透传进去的 options 对象,Super 指向当前构造函数 Vue。初始给 Vue 添加一个 cid,它的值为0,之后每次通过 Vue.extend 创建的子类的 cid 值依次递增。extendOptions._Ctor 是用来缓存子类构造函数,如果已存在就直接返回。

在讲全局 API 的时候,我们有讲到过 Vue.component 这个方法,它是用来注册子组件的

// src/core/global-api/assets.js

export function initAssetRegisters(Vue: GlobalAPI) {
  ASSET_TYPES.forEach(type => {
    Vue[type] = function(
      id: string,
      definition: Function | Object
    ): Function | Object | void {
      ...
      if (type === 'component' && isPlainObject(definition)) {
        definition.name = definition.name || id
        definition = this.options._base.extend(definition)
      }
    }  
  })
}

如果当 type = 'component'definition 是一个对象时,比如:

Vue.component('my-component-name', {  })

就会执行 this.options._base.extend(definition),相当于是 Vue.extend(definition),这里的 name 就是 my-components-name。所以在 Vue.extend 中下面这段代码是用来校验组件名称是否规范用的。

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

再下面就是继承部分了,创建 Sub 构造函数,同时把父类的原型赋值到 Sub.prototype 上。除了原型链的继承,还把父类的静态属性和方法也一并继承了下来:

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

最终返回子类。

小结

我们可以看下,现在子类 Sub 上已经包含了哪些属性和方法:

Sub.cid
Sub.options // 包含了父类的 options 和子类本身的 options
Sub.extend
Sub.mixin
Sub.use
Sub.component
Sub.directive
Sub.filter

// 新增
Sub.super = Super
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)

对比了上一篇中父类上列举的属性和方法,除了新增部分,Sub 上没有的属性包括:

Vue.config
Vue.util

Vue.set
Vue.delete
Vue.nextTick
Vue.observable

参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant