-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
computed.ts
135 lines (124 loc) · 3.28 KB
/
computed.ts
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { ProxyRaw, RawProxy, ReactionStack } from '../environment'
import { createAnnotation } from '../internals'
import { buildDataTree } from '../tree'
import {
bindTargetKeyWithCurrentReaction,
runReactionsFromTargetKey,
bindComputedReactions,
hasRunningReaction,
isUntracking,
batchStart,
batchEnd,
releaseBindingReactions,
} from '../reaction'
interface IValue<T = any> {
value?: T
}
export interface IComputed {
<T>(compute: () => T): IValue<T>
<T>(compute: { get?: () => T; set?: (value: T) => void }): IValue<T>
}
export const computed: IComputed = createAnnotation(
({ target, key, value }) => {
const store: IValue = {}
const proxy = {}
const context = target ? target : store
const property = target ? key : 'value'
const getter = getGetter(context)
const setter = getSetter(context)
function getGetter(target: any) {
if (!target) {
if (value && value.get) return value.get
return value
}
const descriptor = Object.getOwnPropertyDescriptor(target, property)
if (descriptor && descriptor.get) return descriptor.get
return getGetter(Object.getPrototypeOf(target))
}
function getSetter(target: any) {
if (!target) {
if (value && value.set) return value.set
return
}
const descriptor = Object.getOwnPropertyDescriptor(target, property)
if (descriptor && descriptor.set) return descriptor.set
return getSetter(Object.getPrototypeOf(target))
}
function compute() {
store.value = getter?.call?.(context)
}
function reaction() {
if (ReactionStack.indexOf(reaction) === -1) {
releaseBindingReactions(reaction)
try {
ReactionStack.push(reaction)
compute()
} finally {
ReactionStack.pop()
}
}
}
reaction._name = 'ComputedReaction'
reaction._scheduler = () => {
reaction._dirty = true
batchStart()
runReactionsFromTargetKey({
target: context,
key: property,
value: store.value,
type: 'set',
})
batchEnd()
}
reaction._isComputed = true
reaction._dirty = true
reaction._context = context
reaction._property = property
ProxyRaw.set(proxy, store)
RawProxy.set(store, proxy)
buildDataTree(target, key, store)
function get() {
if (hasRunningReaction()) {
bindComputedReactions(reaction)
}
if (!isUntracking()) {
//如果允许untracked过程中收集依赖,那么永远不会存在绑定,因为_dirty已经设置为false
if (reaction._dirty) {
reaction()
reaction._dirty = false
}
} else {
compute()
}
bindTargetKeyWithCurrentReaction({
target: context,
key: property,
type: 'get',
})
return store.value
}
function set(value: any) {
try {
batchStart()
setter?.call?.(context, value)
} finally {
batchEnd()
}
}
if (target) {
Object.defineProperty(target, key, {
get,
set,
enumerable: true,
configurable: false,
})
return target
} else {
Object.defineProperty(proxy, 'value', {
set,
get,
})
}
return proxy
}
)