Vue源码解析07--core.instance-Vue实例

1. index.js

src/instance/index.js主要做如下几件事

  1. initMixin()混入(mixin)初始化设置,包括:
    1. initLifeCycle(vm)
    2. initEvents(vm)
    3. initRender(vm)
    4. callHook(vm, 'beforeCreate')
    5. initInjections(vm)
    6. initState(vm)
    7. initProvide(vm)
    8. callHook(vm, 'created')
  2. stateMixin()混入状态
  3. eventsMixin()混入事件机制
  4. lifecycleMixin()混入生命周期
  5. renderMixin()混入渲染器

2. init.js

initMixin(Vue)

initMixin(Vue)混入初始化设置,改造Vue.prototype,赋予_init(options)方法。

需要initInternalComponent(vm, options)resolveConstructorOptions(Ctor)的辅助。

从该段源码分析,可以得知一个Componentcreate阶段的执行步骤:

  1. 设置Component的uid,作为唯一标识
  2. 非prod环境记录开始点位
  3. vm._isVue = true,防止被纳入observe
  4. 如果是组件,初始化其内部设置;不是组件则执行设置的混入(递归混入上层组件的设置)
  5. 非prod环境尝试Proxy API
  6. vm._self = vm暴露自身
  7. 初始化操作
    1. 初始化生命周期、事件机制、Render
    2. 触发beforeCreate钩子,进入生命周期
    3. 初始化Injection、状态、Provide
    4. 触发created钩子,创建完成
  8. 非prod环境记录结束点位,计算创建时间
  9. 对组件进行挂载
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 每次自增1,保持每个component有唯一的_uid
let uid = 0

function initMixin(Vue: Class<Component>) {
// 原型方法,初始化
Vue.prototype._init = function(options: Object) {
const vm: Compnent = this
// 设置component的id
vm._uid = uid++

let startTag, endTag
// 检查环境,debug模式下使用
if(process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
// 记录初始化开始
mark(startTag)
}

// 防止它被observe
vm._isVue = true

// 如果是组件,就初始化内部设置
if(options && options._isComponent) {
initInternalComponent(vm, options)
}
// 不是组件,混入设置
else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}

// 非生产环境下,尝试Proxy API
if(process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}

// 暴露自身
vm._self = vm
// 初始化生命周期、事件机制、Render
initLifeCycle(vm)
initEvents(vm)
initRender(vm)
// 触发beforeCreate钩子,进入生命周期!!!
callHook(vm, 'beforeCreate')
// 初始化Injection、状态、Provide
initInjections(vm)
initState(vm)
initProvide(vm)
// 触发created钩子,创建完成
callHook(vm, 'created')

if(process.env.NODE_ENV !== 'production' && config.performance && mark) {
// Component的名称
vm._name = formatComponentName(vm, false)
// 记录初始化结束
mark(endTag)
// 计算初始化所需的时间,便于性能分析
measure(`vue ${vm._name} init`, startTag, endTag)
}

// 如果有DOM节点,就将其挂载上去
if(vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}

initInternalComponent(vm, options)

initInternalComponent(vm, options)用于初始化组件内部的设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function initInternalComponent(vm: Component, options: InternalComponentOptions) {
const opts = vm.$options = Object.create(vm.constructor.options)

const parentVnode = options._parentVnode
opts.parent = options.parent
opts._parentVnode = parentVnode

// 拷贝父组件的设置
const vnodeComponentOptions = parentVnode.componentOptions
opts.propsData = vnodeComponentOptions.propsData
opts._parentListeners = vnodeComponentOptions.listeners
opts._renderChildren = vnodeComponentOptions.children
opts._componentTag = vnodeComponentOptions.tag

if (options.render) {
opts.render = options.render
opts.staticRenderFns = options.staticRenderFns
}
}

resolveConstructorOptions(Ctor)

resolveConstructorOptions(Ctor)用于处理构造器的设置(主要是递归解决继承的设置)。

需要resolveModifiedOptions(Ctor)dedupe(latest, extended, sealed)的辅助处理

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
54
55
56
57
58
59
60
61
62
function resolveConstructorOptions(Ctor: Class<Component>) {
let options = Ctor.options
if(Ctor.super) {
const superOptions = resolveConstructorOptions(Ctor.super)
const cachedSuperOptions = Ctor.superOptions

// 上级的设置发生改变,需要刷新
if(superOptions !== cachedSuperOptions) {
Ctor.superOptions = superOptions

// 获取修改过的设置组成的键值对
const modifiedOptions = resolveModifiedOptions(Ctor)
if(modifiedOptions) {
// 混入修改过的设置
extend(Ctor.extendOptions, modifiedOptions)
}

// 合并设置
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if(options.name) {
options.components[options.name] = Ctor
}
}
}
return options
}

// 获取改变的设置,返回其构成的对象
function resolveModifiedOptions(Ctor: Class<Component>) {
let modified
const {options: latest, extendOptions: extended, sealedOptins: sealed} = Ctor
for(const key in latest) {
if(latest[key] !== sealed[key]) {
if(!modified) modified = {}
// 考虑value为Array的情况
modified[key] = dedupe(latest[key], extended[key], sealed[key])
}
}
return modified
}

// 对Array的情况也进行处理,防止忽略
function dedupe(latest, extended, sealed) {
// 如果是Array,则需要遍历处理
if(Array.isArray(latest)) {
const res = []
sealed = Array.isArray(sealed) ? sealed : [sealed]
extended = Array.isArray(extended)? extended : [extended]

// 将非sealed设置,但是extended的设置加入到结果中
for(let i = 0; i < latest.length; i++) {
if(extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) {
res.push[latest[i]]
}
}
return res
}
// 不是Array,直接返回latest
else {
return latest
}
}

Vue源码解析06--core.util.options

util/options.js

建模

Options涉及以下几个模型和字段:

  • strats:strategies,设置的覆盖策略
  • defaultStrat:默认的策略
  • data
  • 生命周期钩子
  • assets:component,directive,filter
  • watcher
  • props(验证由props.js完成)
  • methods
  • inject
  • computed
  • provide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 覆盖原设置的策略
const strats = config.optionMergeStrategies

// 默认策略
const defaultStrat = function (parentVal: any, childVal: any): any {
return childVal === undefined
? parentVal
: childVal
}

/**
* 受约束的设置(Options只能在创建实例时候传入)
*/
if (process.env.NODE_ENV !== 'production') {
strats.el = strats.propsData = function (parent, child, vm, key) {
if (!vm) {
warn(
`option "${key}" can only be used during instance ` +
'creation with the `new` keyword.',
)
}
return defaultStrat(parent, child)
}
}

Vue.extend()在options字段的底层实现

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
54
55
56
57
58
59
60
61
/** 
* Vue.extend的底层实现,同时使用了mergeData()来对两个对象进行递归合并
*/
export function mergeDataOrFn(parentVal: any, childVal: any, vm?: Component): ?Function {
// 没有传入vm,说明是函数的合并,返回闭包
if (!vm) {
// 在Vue.extend中,传入的应当为function
if (!childVal) {
return parentVal
}
if (!parentVal) {
return childVal
}

// 如果parentVal和childVal同时存在,那么返回一个闭包
// 闭包执行后返回对两个函数结果进行递归合并的对象
return function mergedDataFn() {
return mergeData(
typeof childVal === 'function' ? childVal.call(this, this) : childVal,
typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal,
)
}
}
// 如果传入vm,说明是实例的合并,返回合并后的结果
else {
return function mergedInstanceDataFn() {
const instanceData = typeof childVal === 'function'
? childVal.call(vm, vm)
: childVal
const defaultData = typeof parentVal === 'function'
? parentVal.call(vm, vm)
: parentVal
if (instanceData) {
return mergeData(instanceData, defaultData)
} else {
return defaultData
}
}
}
}

// 将两个对象合并,碰到引用类型的情况,对其进行递归合并
function mergeData(to: Object, from: ?Object): Object {
if (!from) return to
let key, toVal, fromVal
const keys = Object.keys(from)
for (let i = 0; i < keys.length; i++) {
key = keys[i]
toVal = to[key]
fromVal = from[key]

if (!hasOwn(to, key)) {
// 对于新加入的属性,手动触发Vue.set
set(to, key, fromVal)
} else if (isPlainObject(toVal) && isPlainObject(fromVal)) {
// 同为对象,进行递归
mergeData(toVal, fromVal)
}
}
return to
}

字段的定义与继承(mixin形式)

涉及了创建组件时,常用字段的定义与继承,包括:

  • data
  • 生命周期钩子
  • assets:component,directive,filter
  • watcher
  • props,methods,inject,computed,provide
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* Data字段相关处理
*/
// data字段的生成策略,这一段决定了data要使用function来创建!!!
strats.data = function (parentVal: any, childVal: any, vm?: Component): ?Function {
if (!vm) {
// 提示vm.data对象必须经由function来创建
if (childVal && typeof childVal !== 'function') {
process.env.NODE_ENV !== 'production' && warn(
'The "data" option should be a function ' +
'that returns a per-instance value in component ' +
'definitions.',
vm,
)

return parentVal
}
return mergeDataOrFn(parentVal, childVal)
}

return mergeDataOrFn(parentVal, childVal, vm)
}

/**
* 合并生命周期钩子,返回形式为Array
*/
function mergeHook(parentVal: ?Array<Function>, childVal: ?Function | ?Array<Function>): ?Array<Function> {
return childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
}

LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})

/**
* Assets
* Assets指component,directive和filter
* 创建vm时,要进行三方面的options合并,包括构造器options,实例options和父级options
*/
function mergeAssets(parentVal: ?Object, childVal: ?Object, vm?: Component, key: string): Object {
// 父级放置于原型链上,做对象委托
const res = Object.create(parentVal || null)
if (childVal) {
// 提示类型是否正确
process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm)
// 执行merge
return extend(res, childVal)
} else {
return res
}
}

ASSET_TYPES.forEach(function (type) {
strats[type + 's'] = mergeAssets
})

/**
* Watchers.
* 要保证相同哈希值的Watcher不会去覆盖另一个,就得使用Array的形式
*/
strats.watch = function (parentVal: ?Object, childVal: ?Object, vm?: Component, key: string): ?Object {
// 绕过FireFox的Object.prototype.watch问题
if (parentVal === nativeWatch) parentVal = undefined
if (childVal === nativeWatch) childVal = undefined

if (!childVal) return Object.create(parentVal || null)
if (process.env.NODE_ENV !== 'production') {
assertObjectType(key, childVal, vm)
}
if (!parentVal) return childVal

const ret = {}
extend(ret, parentVal)

// 使用Array来记录watcher,不是Array就做转换
for (const key in childVal) {
let parent = ret[key]
const child = childVal[key]
if (parent && !Array.isArray(parent)) {
parent = [parent]
}
ret[key] = parent
? parent.concat(child)
: Array.isArray(child) ? child : [child]
}
return ret
}

/**
* 其他对象的哈希合并,可以直接覆盖
*/
strats.props =
strats.methods =
strats.inject =
strats.computed = function (parentVal: ?Object, childVal: ?Object, vm?: Component, key: string): ?Object {
if (childVal && process.env.NODE_ENV !== 'production') {
assertObjectType(key, childVal, vm)
}
if (!parentVal) return childVal
const ret = Object.create(null)
extend(ret, parentVal)
if (childVal) extend(ret, childVal)
return ret
}
strats.provide = mergeDataOrFn

字段的规整(normalize)

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* 保证props的写法规整为Object的形式,且内部每个prop都转换为Object
*/
function normalizeProps(options: Object, vm: ?Component) {
const props = options.props
if (!props) return

const res = {}
let i, val, name
// 如果是Array<string>,则转为Object,其键为每个string驼峰式的写法
if (Array.isArray(props)) {
i = props.length
while (i--) {
val = props[i]
if (typeof val === 'string') {
name = camelize(val)
res[name] = {type: null}
} else if (process.env.NODE_ENV !== 'production') {
warn('props must be strings when using array syntax.')
}
}
}
// 如果是Object,只需转换一下非对象形式声明的prop
else if (isPlainObject(props)) {
for (const key in props) {
val = props[key]
name = camelize(key)
res[name] = isPlainObject(val)
? val
: {type: val}
}
} else if (process.env.NODE_ENV !== 'production') {
warn(
`Invalid value for option "props": expected an Array or an Object, ` +
`but got ${toRawType(props)}.`,
vm,
)
}
options.props = res
}

/**
* 将Injections的写法规整为Object的形式
*/
function normalizeInject(options: Object, vm: ?Component) {
const inject = options.inject
if (!inject) return
const normalized = options.inject = {}
if (Array.isArray(inject)) {
for (let i = 0; i < inject.length; i++) {
normalized[inject[i]] = {from: inject[i]}
}
} else if (isPlainObject(inject)) {
for (const key in inject) {
const val = inject[key]
normalized[key] = isPlainObject(val)
? extend({from: key}, val)
: {from: val}
}
} else if (process.env.NODE_ENV !== 'production') {
warn(
`Invalid value for option "inject": expected an Array or an Object, ` +
`but got ${toRawType(inject)}.`,
vm,
)
}
}

/**
* 将函数形式的directives的写法规整为Object的形式
*/
function normalizeDirectives(options: Object) {
const dirs = options.directives
if (dirs) {
for (const key in dirs) {
const def = dirs[key]
if (typeof def === 'function') {
dirs[key] = {bind: def, update: def}
}
}
}
}

全局API:mergeOptions

该API即为Vue.util.mergeOptions()方法

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
* 合并两个option对象,包括props,Injections和directive的合并
*/
export function mergeOptions(parent: Object, child: Object, vm?: Component): Object {
if (process.env.NODE_ENV !== 'production') {
checkComponents(child)
}

if (typeof child === 'function') {
child = child.options
}

// 规整三个字段
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)

// 如果有一些特殊继承规则,则先对父级进行预处理
const extendsFrom = child.extends
if (extendsFrom) {
parent = mergeOptions(parent, extendsFrom, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}

// 合并各个字段
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}

// 套用合并策略,进行某个字段的合并
function mergeField(key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}

return options
}

/**
* 检查components引入的子组件名称是否合法
*/
function checkComponents(options: Object) {
for (const key in options.components) {
validateComponentName(key)
}
}

// 验证组件名称,这一段决定了组件名必须以字母开头,并只能使用字母和连字符
export function validateComponentName(name: string) {
if (!/^[a-zA-Z][\w-]*$/.test(name)) {
warn(
'Invalid component name: "' + name + '". Component names ' +
'can only contain alphanumeric characters and the hyphen, ' +
'and must start with a letter.',
)
}
if (isBuiltInTag(name) || config.isReservedTag(name)) {
warn(
'Do not use built-in or reserved HTML elements as component ' +
'id: ' + name,
)
}
}

其他helper函数

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
// 断言是否为Object
function assertObjectType(name: string, value: any, vm: ?Component) {
if (!isPlainObject(value)) {
warn(
`Invalid value for option "${name}": expected an Object, ` +
`but got ${toRawType(value)}.`,
vm,
)
}
}

/**
* 检查options中是否有某个asset(component,directive,filter)
* 指示子组件实例是否要访问其上层链中定义的asset
*/
export function resolveAsset(options: Object, type: string, id: string, warnMissing?: boolean): any {
if (typeof id !== 'string') {
return
}
const assets = options[type]
// 用三种拼写形式去检查,
if (hasOwn(assets, id)) return assets[id]
const camelizedId = camelize(id)
if (hasOwn(assets, camelizedId)) return assets[camelizedId]
const PascalCaseId = capitalize(camelizedId)
if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]

const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
warn(
'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
options,
)
}
return res
}

Vue源码解析05--core.util.props

util/props.js

props.js主要暴露一个validateProp()方法,将vm中的单个Prop合法化。如遇到不合法的地方,则发出警告。

建模

一个Prop有四个属性:

  • type:Prop的类型,传入类型构造器(多个情况下传Array)
  • default:默认值
  • required:是否一定要传入
  • validator:验证函数
1
2
3
4
5
6
7
// property的选项(注册props时候填写)
type PropOptions = {
type: Function | Array<Function> | null,
default: any,
required: ?boolean,
validator: ?Function
};

获取default值

需要注意,如果typeObjectArray,就必须使用工厂模式来返回初始值,即default为一个Function

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
/**
* 获取某个Prop的默认值。
* 注意引用类型要使用工厂模式,然后执行返回
*/
function getPropDefaultValue(vm: ?Component, prop: PropOptions, key: string): any {
// 没有设置default,则为undefined
if (!hasOwn(prop, 'default')) {
return undefined
}

const def = prop.default
// 提示Object和Array要用工厂模式来生成,例如 ()=>[]
if (process.env.NODE_ENV !== 'production' && isObject(def)) {
warn(
'Invalid default value for prop "' + key + '": ' +
'Props with type Object/Array must use a factory function ' +
'to return the default value.',
vm,
)
}

// 如果一开始propsData[key]为undefined,直接返回_props[key],防止触发watcher
if (vm && vm.$options.propsData &&
vm.$options.propsData[key] === undefined &&
vm._props[key] !== undefined
) {
return vm._props[key]
}

// 针对引用类型的工厂模式,执行后返回结果
return typeof def === 'function' && getType(prop.type) !== 'Function'
? def.call(vm)
: def
}

检查值是否有效(type检查与validator检查)

检查输入的值是否符合type中声明的类型,以及能否通过定制的validator

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* 断言某个prop是否有效,隐含了type和validator的检查
*/
function assertProp(prop: PropOptions, name: string, value: any, vm: ?Component, absent: boolean) {
// 声明了required却没有使用的,发出警告
if (prop.required && absent) {
warn(
'Missing required prop: "' + name + '"',
vm,
)
return
}
if (value == null && !prop.required) {
return
}
let type = prop.type
let valid = !type || type === true
const expectedTypes = []

// 检查每个类型,查看value是否符合其中一个
if (type) {
if (!Array.isArray(type)) {
type = [type]
}
for (let i = 0; i < type.length && !valid; i++) {
const assertedType = assertType(value, type[i])
expectedTypes.push(assertedType.expectedType || '')
valid = assertedType.valid
}
}

// 类型不合法,发出警告
if (!valid) {
warn(
`Invalid prop: type check failed for prop "${name}".` +
` Expected ${expectedTypes.map(capitalize).join(', ')}` +
`, got ${toRawType(value)}.`,
vm,
)
return
}

// 套用validator,检查value是否合法
const validator = prop.validator
if (validator) {
if (!validator(value)) {
warn(
'Invalid prop: custom validator check failed for prop "' + name + '".',
vm,
)
}
}
}

/**
* 检查value是否符合某个类型
*/
const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/
function assertType(value: any, type: Function): {
valid: boolean;
expectedType: string;
} {
let valid
const expectedType = getType(type)
// 基本类型的处理
if (simpleCheckRE.test(expectedType)) {
const t = typeof value
valid = t === expectedType.toLowerCase()

// 也有可能是基本类型构造器的实例
if (!valid && t === 'object') {
valid = value instanceof type
}
}
// 如果是Object,则检查value是否为纯Object
else if (expectedType === 'Object') {
valid = isPlainObject(value)
}
// 如果是Array,检查value是否为Array
else if (expectedType === 'Array') {
valid = Array.isArray(value)
}
// 如果都不是,检查value是否为type的实例
else {
valid = value instanceof type
}
return {
valid,
expectedType,
}
}

核心:检查某个Prop,如果合法则将其加入observe并返回,否则发出警告

该方法将提供给Component使用,用于验证Prop

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
/**
* 检查是否符合prop描述,获得默认值,然后加入观察
*/
export function validateProp(key: string, propOptions: Object, propsData: Object, vm?: Component): any {
const prop = propOptions[key]
// 检查该prop是否存在
const absent = !hasOwn(propsData, key)
let value = propsData[key]
// 在prop.type中寻找Boolean类型对应的索引
const booleanIndex = getTypeIndex(Boolean, prop.type)

// 如果有Boolean类型
if (booleanIndex > -1) {
// default没设置,默认为false
if (absent && !hasOwn(prop, 'default')) {
value = false
}
// 带有string,但是Boolean靠前,将空字符串转为true
else if (value === '' || value === hyphenate(key)) {
const stringIndex = getTypeIndex(String, prop.type)
if (stringIndex < 0 || booleanIndex < stringIndex) {
value = true
}
}
}

// 检查默认值
if (value === undefined) {
// 获取默认值,主要是针对引用类型的处理
value = getPropDefaultValue(vm, prop, key)
// 将该Prop纳入观察
const prevShouldObserve = shouldObserve
toggleObserving(true)
observe(value)
toggleObserving(prevShouldObserve)
}

// 非生产环境下,验证该prop的类型、required、validator,对有问题的地方进行断言
if (
process.env.NODE_ENV !== 'production' &&
// Weex下跳过检查
!(__WEEX__ && isObject(value) && ('@binding' in value))
) {
assertProp(prop, key, value, vm, absent)
}
return value
}

其他的helper函数

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
/**
* 使用类型构造器的函数名来检查类型
*/
// 调取函数名(或是class类名)
function getType(fn) {
const match = fn && fn.toString().match(/^\s*function (\w+)/)
return match ? match[1] : ''
}

// 检查类名是否相同,从而确认为相同类
function isSameType(a, b) {
return getType(a) === getType(b)
}

// 获取指定类型的索引
function getTypeIndex(type, expectedTypes): number {
// 如果expectedTypes不是Array,也就是只传入了一个类型,检查类名
if (!Array.isArray(expectedTypes)) {
return isSameType(expectedTypes, type) ? 0 : -1
}
// 如果expectedTypes是Array,遍历expectedTypes来查找type
for (let i = 0, len = expectedTypes.length; i < len; i++) {
if (isSameType(expectedTypes[i], type)) {
return i
}
}
return -1
}