# 组件内部状态
现在我们已经知道,vue._init()大致的执行流程,那么其中的state到底是怎么实现的呢?
# initProps
// core/instance/state.js
function initProps (vm: Component, propsOptions: Object) {
// 传入的props对象
const propsData = vm.$options.propsData || {}
// props实际存储的位置,_props
const props = vm._props = {}
// 缓存prop键,以便将来的道具更新可以使用Array进行迭代而不是动态对象键枚举。
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// 如果是根节点,那么不可观察
if (!isRoot) {
toggleObserving(false)
}
// 遍历propsData
for (const key in propsOptions) {
// 加入缓存
keys.push(key)
// 验证props的值,得到正确的值
const value = validateProp(key, propsOptions, propsData, vm)
// 数据劫持
defineReactive(props, key, value)
// 把props中的每一个值,代理到实例上
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
// 状态恢复
toggleObserving(true)
}
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
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
从上面代码分析可以看出
props的值在被验证之后,被挂载到内部变量_props中- 设置可以通过
this访问的_props代理
# initMethods
function initMethods (vm: Component, methods: Object) {
for (const key in methods) {
vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
}
}
1
2
3
4
5
2
3
4
5
从上面代码分析可以看出
- 遍历methods中所有的方法
- 通过
bind方法使得方法中this指向当前实例 - 把处理后的方法挂载到当前实例
(this)
# initData
function initData (vm: Component) {
// 获取data选项
let data = vm.$options.data
// 获取data的指,并且赋值给_data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// 容错处理
if (!isPlainObject(data)) {
data = {}
}
// 获取所有的key
const keys = Object.keys(data)
let i = keys.length
while (i--) {
const key = keys[i]
// 如果key不是以$或者_开头
if (!isReserved(key)) {
// 把_data中的每一个值,代理到实例上
proxy(vm, `_data`, key)
}
}
// 为data添加观察者
observe(data, true)
}
export function getData (data: Function, vm: Component): any {
// #7573 disable dep collection when invoking data getters
pushTarget()
try {
// 执行data方法,这里的this指向实例
return data.call(vm, vm)
} catch (e) {
handleError(e, vm, `data()`)
return {}
} finally {
popTarget()
}
}
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
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
从上面代码分析可以看出
- 首先获取了传入的
data选项中的值,并且把值挂载到内部变量_data中 - 设置可以通过
this访问的_data代理 - 通过调用
observe来观察data中的值
# initComputed
// 默认的配置
const computedWatcherOptions = { lazy: true }
function initComputed (vm: Component, computed: Object) {
// 内部变量
const watchers = vm._computedWatchers = Object.create(null)
// 服务端渲染?
const isSSR = isServerRendering()
for (const key in computed) {
// 用户穿入的值
const userDef = computed[key]
// 获取getter
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (!isSSR) {
// 每一个计算属性都将对应一个 watcher 实例
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
// 定义计算属性
if (!(key in vm)) {
defineComputed(vm, key, userDef)
}
}
}
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
export function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
// 如果不是服务端渲染,那么需要缓存
const shouldCache = !isServerRendering()
if (typeof userDef === 'function') {
// 如果传入的是方法,那么包装
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: userDef
sharedPropertyDefinition.set = noop
} else {
// 如果传入的是对象,那么包装
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: userDef.get
: noop
sharedPropertyDefinition.set = userDef.set
? userDef.set
: noop
}
// 把key代理到当前实例
Object.defineProperty(target, key, sharedPropertyDefinition)
}
function createComputedGetter (key) {
return function computedGetter () {
// 获取对应的watcher
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
// 懒监听
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
// 添加依赖
watcher.depend()
}
return watcher.value
}
}
}
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
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
- 定义内部变量
_computedWatchers, - 在
_computedWatchers对象里面是key相对应的watcher实例,以便于收集compute中的依赖 - 把
computed的key挂载到当前vue实例上
# initWatch
- 直接调用
this.$watch生成watcher实例
function initWatch (vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key]
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
createWatcher(vm, key, handler)
}
}
}
function createWatcher (
vm: Component,
expOrFn: string | Function,
handler: any,
options?: Object
) {
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
if (typeof handler === 'string') {
handler = vm[handler]
}
return vm.$watch(expOrFn, handler, 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
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
# 问题
- 问题: 为什么
props直接用了defineReactive,而data却用了oberve?props只是做了单个值的观察data的只属性如果也是对象的话,也做了子属性值的数据观察
← 理解内部状态前的准备 组件内部状态小结 →