-
-
Notifications
You must be signed in to change notification settings - Fork 8.6k
/
Copy pathcomponentRenderContext.ts
122 lines (110 loc) · 3.54 KB
/
componentRenderContext.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
import type { ComponentInternalInstance } from './component'
import { devtoolsComponentUpdated } from './devtools'
import { setBlockTracking } from './vnode'
/**
* mark the current rendering instance for asset resolution (e.g.
* resolveComponent, resolveDirective) during render
*/
export let currentRenderingInstance: ComponentInternalInstance | null = null
export let currentScopeId: string | null = null
/**
* Note: rendering calls maybe nested. The function returns the parent rendering
* instance if present, which should be restored after the render is done:
*
* ```js
* const prev = setCurrentRenderingInstance(i)
* // ...render
* setCurrentRenderingInstance(prev)
* ```
*/
export function setCurrentRenderingInstance(
instance: ComponentInternalInstance | null,
): ComponentInternalInstance | null {
const prev = currentRenderingInstance
currentRenderingInstance = instance
currentScopeId = (instance && instance.type.__scopeId) || null
// v2 pre-compiled components uses _scopeId instead of __scopeId
if (__COMPAT__ && !currentScopeId) {
currentScopeId = (instance && (instance.type as any)._scopeId) || null
}
return prev
}
/**
* Set scope id when creating hoisted vnodes.
* @private compiler helper
*/
export function pushScopeId(id: string | null): void {
currentScopeId = id
}
/**
* Technically we no longer need this after 3.0.8 but we need to keep the same
* API for backwards compat w/ code generated by compilers.
* @private
*/
export function popScopeId(): void {
currentScopeId = null
}
/**
* Only for backwards compat
* @private
*/
export const withScopeId = (_id: string): typeof withCtx => withCtx
export type ContextualRenderFn = {
(...args: any[]): any
_n: boolean /* already normalized */
_c: boolean /* compiled */
_d: boolean /* disableTracking */
_ns: boolean /* nonScoped */
}
/**
* Wrap a slot function to memoize current rendering instance
* @private compiler helper
*/
export function withCtx(
fn: Function,
ctx: ComponentInternalInstance | null = currentRenderingInstance,
isNonScopedSlot?: boolean, // __COMPAT__ only
): Function {
if (!ctx) return fn
// already normalized
if ((fn as ContextualRenderFn)._n) {
return fn
}
const renderFnWithContext: ContextualRenderFn = (...args: any[]) => {
// If a user calls a compiled slot inside a template expression (#1745), it
// can mess up block tracking, so by default we disable block tracking and
// force bail out when invoking a compiled slot (indicated by the ._d flag).
// This isn't necessary if rendering a compiled `<slot>`, so we flip the
// ._d flag off when invoking the wrapped fn inside `renderSlot`.
if (renderFnWithContext._d) {
setBlockTracking(-1)
}
const prevInstance = setCurrentRenderingInstance(ctx)
let res
try {
res = fn(...args)
} finally {
setCurrentRenderingInstance(prevInstance)
if (renderFnWithContext._d) {
setBlockTracking(1)
}
}
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
devtoolsComponentUpdated(ctx)
}
return res
}
// mark normalized to avoid duplicated wrapping
renderFnWithContext._n = true
// mark this as compiled by default
// this is used in vnode.ts -> normalizeChildren() to set the slot
// rendering flag.
renderFnWithContext._c = true
// disable block tracking by default
renderFnWithContext._d = true
// compat build only flag to distinguish scoped slots from non-scoped ones
if (__COMPAT__ && isNonScopedSlot) {
renderFnWithContext._ns = true
}
return renderFnWithContext
}