3-2-04-Vue.js 源码阅读-初始化过程-静态成员

Vue 初始化的过程-静态成员

在 src/core/index.js 这个文件中调用 initGlobalAPI 这个函数中初始化了 vue 的大多数静态成员。

initGlobalAPI

  • 首先在方法中通过 defineProperty 给 Vue 挂载一个 config 对象属性
// config
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
  configDef.set = () => {
    warn(
      'Do not replace the Vue.config object, set individual fields instead.'
    )
  }
}
// 初始化 Vue.config 对象
Object.defineProperty(Vue, 'config', configDef)
  • 然后是挂载静态方法 set/delete/nextTick
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
  • 创建 options 对象,为了提高性能,将其原型设置为 null
// ASSET_TYPES = ['components', 'directives', 'filters']
// 初始化 Vue.options 对象,设置对象的原型为 null(提高性能) ,并给其扩展
// components/directives/filters/
// Vue 注册的全局的组件指令和过滤器都会分别保存在这三个对象下
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
  Vue.options[type + 's'] = Object.create(null)
})
  • 在 options 下面记录 Vue 构造函数
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue

这个构造函数后面会用到,这里先不写,后面什么时候看到这个地方来再来填坑。

  • 调用 extend 方法将 builtInComponents 的所有属性拷贝给 Vue.options.components 这个对象。
// 设置 keep-alive 组件
extend(Vue.options.components, builtInComponents)

这个地方就是注册了全局组件 keep-alive

  • 注册 Vue.use() 静态方法
// 注册 Vue.use() 用来注册插件
initUse(Vue)

下面是 initUse 的实现源码

/* @flow */

import { toArray } from '../util/index'

export function initUse (Vue: GlobalAPI) {
  Vue.use = function (plugin: Function | Object) {
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    if (installedPlugins.indexOf(plugin) > -1) {
      return this
    }

    // additional parameters
    // 把数组中的第一个元素(plugin)删除
    const args = toArray(arguments, 1)
    // 把 this(Vue) 插到第一个元素的位置
    args.unshift(this)
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args)
    }
    installedPlugins.push(plugin)
    return this
  }
}

  1. 在 initUse 中首先判断 plugins 的缓存数组是否存在,如果不存在,创建插件缓存数组,以存储已经注册过的插件防止重复注册。
  2. 判断插件是否已经注册过了,如果注册过了 return false
  3. 存储将要传给即将注册的插件的参数,并将 Vue 实例 unshift 到参数数组中。
  4. 判断 use 的插件是否有 install 方法,如果有使用 apply 调用这个 install 方法,并将前面存储好的参数数组作为参数传给 install 方法。
  5. 如果没有 install 方法,而是直接传入的一个函数,那就直接通过 apply 调用这个函数,并将前面存储好的参数数组作为参数传给这个函数。
  6. 最后将注册的插件 push 到插件缓存数组中,然后将 Vue 实例返回。

通过上述源码阅读,可以发现,Vue 插件可以是一个函数的形式,也可以是含有 install 方法的对象。

  • 注册 Vue.mixin() 静态方法
// 注册 Vue.mixin() 实现混入
initMixin(Vue)

下面是 initMixin 的源码

/* @flow */

import { mergeOptions } from '../util/index'

export function initMixin (Vue: GlobalAPI) {
  Vue.mixin = function (mixin: Object) {
    this.options = mergeOptions(this.options, mixin)
    return this
  }
}

在这个方法中直接调用 mergeOptions 这个方法,将我们要混入的选项,合并到 Vue 的全局选项 options 中

  • 注册 Vue.extend() 静态方法
// 注册 Vue.extend() 基于传入的 options 返回一个组件的构造函数
initExtend(Vue)

下面是 initExtend 的源码

/* @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) {
  /**
   * 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 || {}
    // Vue 构造函数
    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) {
      // 调用 _init() 初始化
      this._init(options)
    }
    // 原型继承自 Vue
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
    // 合并 options
    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) {
      // 把组件的构造函数保存到 Ctor.options.components.comp = Ctor
      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
    // 把组件的构造函数缓存到 options._Ctor
    cachedCtors[SuperId] = Sub
    return Sub
  }
}

function initProps (Comp) {
  const props = Comp.options.props
  for (const key in 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])
  }
}

这个文件的代码比较多,主要就是创建了一个构造函数 VueComponent 即组件的构造函数,并且这个构造函数原型继承自 Vue,所以所有的 Vue 组件都是继承自 Vue 。然后再构造函数中调用了继承自 Vue 的 _init 这个方法创建组件实例。最终将这个构造函数返回。

  • 注册 Vue.directive()、Vue.component()、Vue.filter() 静态方法
// 注册 Vue.directive()、Vue.component()、Vue.filter()
initAssetRegisters(Vue)

initAssetRegisters 方法的源码如下

/* @flow */

import { ASSET_TYPES } from 'shared/constants'
import { isPlainObject, validateComponentName } from '../util/index'

export function initAssetRegisters (Vue: GlobalAPI) {
  /**
   * Create asset registration methods.
   */
  // 遍历 ASSET_TYPES 数组,为 Vue 定义相应方法
  // ASSET_TYPES 包括了 directive、component、filter
  ASSET_TYPES.forEach(type => {
    Vue[type] = function (
      id: string,
      definition: Function | Object
    ): Function | Object | void {
      if (!definition) {
        return this.options[type + 's'][id]
      } else {
        /* istanbul ignore if */
        if (process.env.NODE_ENV !== 'production' && type === 'component') {
          validateComponentName(id)
        }
        // Vue.component('comp', { template: '' })
        if (type === 'component' && isPlainObject(definition)) {
          definition.name = definition.name || id
          // 把组件的配置转换为组件的构造函数
          definition = this.options._base.extend(definition)
        }
        if (type === 'directive' && typeof definition === 'function') {
          definition = { bind: definition, update: definition }
        }
        // 全局注册,存储资源并赋值
        // this.options['components']['comp'] = definition
        this.options[type + 's'][id] = definition
        return definition
      }
    }
  })
}

通过遍历 ASSET_TYPES 这个常量数组,分别给 Vue 注册三个静态方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值