vue源码分析(五) 静态属性和方法

10/5/2018 vue源码分析面试

# 1. 概述

​ 在上一章——vue源码分析(四) 实例属性和方法 (opens new window),中我们分析 Vue 的实例属性和方法,接下来我们分析 Vue 的静态属性和方法。

​ 我们再回到 initGlobalAPI 定义执行的地方,如下:

​ 源码目录: src/core/global-api/index.js


export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () => config // Vue.config 获取 config 全局变量
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => { // 设置Vue.config时直接报错,即不允许设置Vue.config值
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  // 通过ES5的defineProperty设置Vue的config的访问器属性
  // 获取Vue.config时会执行configDef.get函数
  // 设置Vue.config时会执行configDef.set函数
  Object.defineProperty(Vue, 'config', configDef)

  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }

  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // 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(Vue.options.components, builtInComponents)

  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 2. 静态属性和方法

initGlobalAPI 方法的作用就是给初始化全局 API 即给 Vue 构造函数添加静态属性和方法,下面我们逐一分析。

# 2.1 config

initGlobalAPI 方法一开始就是添加全局配置,全局配置是一个静态只读属性。具体代码实现如下:

​ 源码目录: src/core/global-api/index.js

// config
const configDef = {}
configDef.get = () => config // Vue.config 获取 config 全局变量
if (process.env.NODE_ENV !== 'production') {
  configDef.set = () => { // 设置Vue.config时直接报错,即不允许设置Vue.config值
    warn(
      'Do not replace the Vue.config object, set individual fields instead.'
    )
  }
}
// 通过ES5的defineProperty设置Vue的config的访问器属性
// 获取Vue.config时会执行configDef.get函数
// 设置Vue.config时会执行configDef.set函数
Object.defineProperty(Vue, 'config', configDef)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

说明:关于全局配置我们在这里不做详细分析,后面都会讲解到,请移步到这里 (opens new window)学习。

# 2.2 util

​ 接下来是给 Vue 添加 util 静态属性对象,包括 warnextendmergeOptionsdefineReactive 四个属性。代码如下:

源码目录: `src/core/global-api/index.js`
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
Vue.util = {
  warn,
  extend,
  mergeOptions,
  defineReactive
}
1
2
3
4
5
6
7
8
9

# 2.3 set

​ 然后给 Vue 添加 set 静态方法,主要作用是向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。代码如下:

​ 源码目录: src/core/global-api/index.js

Vue.set = set
1

# 2.4 delete

​ 接着给 Vue 添加 delete 静态方法,主要作用是删除对象的属性。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到属性被删除的限制,但是你应该很少会使用它。代码如下:

​ 源码目录: src/core/global-api/index.js

Vue.delete = del
1

# 2.5 nextTick

​ 然后给 Vue 添加 nextTick 静态方法,主要作用是在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。代码如下:

​ 源码目录: src/core/global-api/index.js

Vue.nextTick = nextTick
1

# 2.6 observable

​ 接下来给 Vue 添加 observable 静态属性,主要作用是让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。代码如下:

​ 源码目录: src/core/global-api/index.js

Vue.observable = <T>(obj: T): T => {
  observe(obj)
  return obj
}
1
2
3
4

说明:关于 observable 具体用法,请移步到这里 (opens new window)学习。

# 2.7 options

​ 继续执行代码,给 Vue 添加 options 静态属性。这里要注意的是 Vue.options,现在它还只是一个空的对象,通过 Object.create(null) 创建。

​ 源码目录: src/core/global-api/index.js

Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
  Vue.options[type + 's'] = Object.create(null)
})

// 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
1
2
3
4
5
6
7
8

​ 接下来是给 options 添加属性,通过遍历 ASSET_TYPES 添加属性,首先我们看一下 ASSET_TYPES 定义,如下:

​ 源码目录: src/shared/constants.js

export const ASSET_TYPES = [
  'component',
  'directive',
  'filter'
]
1
2
3
4
5

执行完循环,options 就不是一个空对象了,如下:

Vue.options = {
	components: Object.create(null),
	directives: Object.create(null),
	filters: Object.create(null),
	_base: Vue
}
1
2
3
4
5
6

# 3. extend函数

​ 接下来执行如下代码:

​ 源码目录: src/core/global-api/index.js

extend(Vue.options.components, builtInComponents)
1

extend 的作用是将 builtInComponents 的属性混合到 Vue.options.components 中,其中 builtInComponents 的定义如下:

​ 源码目录: src/core/components/index.js

import KeepAlive from './keep-alive'

export default {
  KeepAlive
}
1
2
3
4
5

​ 从源码我们分析可以得出 builtInComponents 就是 Vue 内置组件 KeepAlive,所以最终合并后的结果为,如下:

Vue.options = {
	components: {
    KeepAlive
  },
	directives: Object.create(null),
	filters: Object.create(null),
	_base: Vue
}
1
2
3
4
5
6
7
8

说明:关于 extend 方法这里不做详细分析,后面会详细分析。

# 4. initUse

​ 我们先从 initUse 的定义入手,代码如下:

​ 源码目录: src/core/global-api/use.js

export function initUse (Vue: GlobalAPI) {
  Vue.use = function (plugin: Function | Object) { /* */ }
}
1
2
3

​ 该方法的作用是在 Vue 构造函数上添加 use 静态方法,也就是 Vue.use 这个全局API,这个方法的作用是安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将Vue 作为参数传入。

说明:关于 use 方法如何实现的这里不做详细分析,后面会详细分析。

提示:关于 use 的使用可以移步到这里 (opens new window)学习。

# 5. initMixin

​ 接下来我们看一下 initMixin 的定义入手,代码如下:

​ 源码目录: src/core/global-api/mixin.js

export function initMixin (Vue: GlobalAPI) {
  Vue.mixin = function (mixin: Object) { /* */ }
}
1
2
3

initMixin 方法的作用是,在 Vue 上添加 mixin 静态方法,这个方法的作用是全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。插件作者可以使用混入,向组件注入自定义的行为。不推荐在应用代码中使用

说明:关于 mixin 方法如何实现的这里不做详细分析,后面会详细分析。

提示:关于 use 的使用可以移步到这里 (opens new window)学习。

# 6. initExtend

​ 接着我们看一下 initExtend 的定义,代码如下:

​ 源码目录: src/core/global-api/extend.js

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

  /**
   * Class inheritance
   */
  Vue.extend = function (extendOptions: Object): Function { /* */ }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

initExtend 方法在 Vue 上添加了 Vue.cid 静态属性,和 Vue.extend 静态方法。Vue.extend 这个方法的作用是使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。

说明:关于 use 方法如何实现的这里不做详细分析,后面会详细分析。

提示:关于 use 的使用可以移步到这里 (opens new window)学习。

# 7. initAssetRegisters

​ 最后我们来看 initAssetRegisters 的定义,代码如下:

​ 源码目录: src/core/global-api/assets.js

export function initAssetRegisters (Vue: GlobalAPI) {
  /**
   * Create asset registration methods.
   */
  ASSET_TYPES.forEach(type => {
    Vue[type] = function (
      id: string,
      definition: Function | Object
    ): Function | Object | void { /* */ }
  })
}

1
2
3
4
5
6
7
8
9
10
11
12

​ 同 options 添加属性一样,通过遍历 ASSET_TYPESVue 添加 componentdirectivefilter 三个静态方法。他们的作用分别为:

  1. component:注册或获取全局组件。注册还会自动使用给定的 id 设置组件的名称。
  2. directive:注册或获取全局指令。
  3. filter:注册或获取全局过滤器。

提示:关于这三个的使用可以移步到component (opens new window)directive (opens new window)filter (opens new window)学习。

# 8. 其他

​ 我们再回到 initGlobalAPI 函数执行的地方,如下:

​ 源码目录: src/core/index.js

initGlobalAPI(Vue)

// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
})
1
2
3
4
5
6

​ 执行完 initGlobalAPI 函数,接着又在 Vue 构造函数上定义了 FunctionalRenderContext 静态属性,是为了在 ssr 中使用它。

# 9. 总结

​ 通过我们上面的分析,我们可以总结出 Vue 静态属性和方法即 Vue 全局 API,接下来我们已思维导图的方式总结。

说明:在本章节中没有遇到的 Vue 全局 API 我们也会在以后的分析总归纳进来,会在思维导图中标在出来具体的章节。

vue实例属性和方法