vue2源码(一)-- Vue.extend

目录

  • 作用
  • 使用场景
  • 原理分析
  • 源码

作用

使用基础的vue构造器,创建一个子类。参数是包含组件选项的对象。其中data必须是函数

使用场景

1、所有自定义组件都是通过extend方法构造出来的

2、实现js调用组件,element-uiMessageMessageBox等js调用组件,都是通过Vue.extend实现的

原理分析

1、获取父类的cid

2、从对象参数中获取缓存池,根据父类的cid判断缓存池中是否已经在之前创建过改子类。是,就直接返回缓存的。这是为了vue的性能考虑的。对于同一个组件选项对象,反复调用vue.extend返回的是同一个结果的

3、获取组件的 name 字段,校验组件名字是否合法

4、通过函数的方式创建一个sub类,并将父类的原型链继承到子类中(原型链继承方式),同时还需要修正constructor的指向

5、将父类的options字段和传入组件对象选项进行合并,并保存在子类suboptions字段中

6、将父类保存到子类的 super 字段中,确保子类能拿到父类

7、初始化props,实际上是代理到_props属性。根组件的 props 是在这里进行数据劫持的。好处就是不用为每个组件的实例都做一层 proxy,这是一种优化手段

8、初始化computed

9、将父类的extendmixin等全局 API 添加到子类中

10、如果name字段存在,则给子类sub添加name字段,方便进行组件递归

11、新增superOptionsextendOptions等子类独有的属性

12、将构造出来的子类sub放进缓存池,key值为父类的cid

13、返回子类sub

源码

源码位于src/core/global-api/extend.js

/* @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) {
  // 每个vue实例都有一个唯一标识
  Vue.cid = 0
  let cid = 1

  // 类继承,作用是创建一个继承自vue类的子类,参数接收的是组件选项的对象
  // extendOptions:用户传入的组件选项参数
  Vue.extend = function (extendOptions: Object): Function {
    // 用户传入的一个包含组件选项的对象参数
    extendOptions = extendOptions || {}
    // 父类,即基础的vue类
    const Super = this
    // 父类id,无论是基础vue类还是继承的vue类,都有一个唯一标识
    const SuperId = Super.cid
    // 缓存池,用于缓存创建出来的类
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    // 缓存池
    if (cachedCtors[SuperId]) {
      // 这一步是为了vue性能考虑,反复调用其实返回的是同一个结果
      // 已经创建过的不需要在创建
      // 避免多次执行 Vue.extend 的时候对同一个子组件重复构造。
      return cachedCtors[SuperId]
    }
    // 获取组件选项的name字段
    const name = extendOptions.name || Super.options.name
    // 校验组件名是否合法
    if (process.env.NODE_ENV !== 'production' && name) {
      validateComponentName(name)
    }

    // 创建一个vue类的子类,这个类即将要继承基础vue类
    const Sub = function VueComponent (options) {
      this._init(options)
    }
    // 原型链继承方式
    // 将父类的原型继承到子类
    Sub.prototype = Object.create(Super.prototype)
    // 修正constructor的指向
    Sub.prototype.constructor = Sub
    // id自增,保证唯一标识
    Sub.cid = cid++
    // 父类的options和传入的子类options进行合并
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    // 将父类保存到子类的super字段中,确保子类能拿到父类
    Sub['super'] = Super

    // 初始化props,根组件的props是在这里进行数据劫持的。好处就是不用为每个组件的实例都做一层proxy,这是一种优化手段
    if (Sub.options.props) {
      // 初始化props其实就是代理到原型的_props属性
      initProps(Sub)
    }
    // 初始化computed
    if (Sub.options.computed) {
      initComputed(Sub)
    }

    // 将父类的一些属性添加到子类中
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    // 'component',
    // 'directive',
    // 'filter'
    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
    return Sub
  }
}

function initProps (Comp) {
  const props = Comp.options.props
  for (const key in props) {
    // 将props代理到原型上面的_props
    proxy(Comp.prototype, `_props`, key)
  }
}

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

你可能感兴趣的:(笔记,文章,原理,源码,vue.js,javascript)