Skip to content

Commit b6cdd56

Browse files
committed
wip: template binding optimization
1 parent b51b79f commit b6cdd56

File tree

12 files changed

+81
-30
lines changed

12 files changed

+81
-30
lines changed

packages/compiler-core/src/codegen.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ export interface CodegenResult {
6464
map?: RawSourceMap
6565
}
6666

67-
export interface CodegenContext extends Required<CodegenOptions> {
67+
export interface CodegenContext
68+
extends Omit<Required<CodegenOptions>, 'bindingMetadata'> {
6869
source: string
6970
code: string
7071
line: number
@@ -204,16 +205,19 @@ export function generate(
204205
}
205206

206207
// enter render function
208+
const optimizeSources = options.bindingMetadata
209+
? `, $props, $setup, $data, $options`
210+
: ``
207211
if (!ssr) {
208212
if (genScopeId) {
209213
push(`const render = ${PURE_ANNOTATION}_withId(`)
210214
}
211-
push(`function render(_ctx, _cache) {`)
215+
push(`function render(_ctx, _cache${optimizeSources}) {`)
212216
} else {
213217
if (genScopeId) {
214218
push(`const ssrRender = ${PURE_ANNOTATION}_withId(`)
215219
}
216-
push(`function ssrRender(_ctx, _push, _parent, _attrs) {`)
220+
push(`function ssrRender(_ctx, _push, _parent, _attrs${optimizeSources}) {`)
217221
}
218222
indent()
219223

packages/compiler-core/src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ export {
66
ParserOptions,
77
TransformOptions,
88
CodegenOptions,
9-
HoistTransform
9+
HoistTransform,
10+
BindingMetadata
1011
} from './options'
1112
export { baseParse, TextModes } from './parse'
1213
export {

packages/compiler-core/src/options.ts

+10
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ export type HoistTransform = (
5757
parent: ParentNode
5858
) => void
5959

60+
export interface BindingMetadata {
61+
[key: string]: 'data' | 'props' | 'setup' | 'options'
62+
}
63+
6064
export interface TransformOptions {
6165
/**
6266
* An array of node transforms to be applied to every AST node.
@@ -122,6 +126,11 @@ export interface TransformOptions {
122126
* `ssrRender` option instead of `render`.
123127
*/
124128
ssr?: boolean
129+
/**
130+
* Optional binding metadata analyzed from script - used to optimize
131+
* binding access when `prefixIdentifiers` is enabled.
132+
*/
133+
bindingMetadata?: BindingMetadata
125134
onError?: (error: CompilerError) => void
126135
}
127136

@@ -169,6 +178,7 @@ export interface CodegenOptions {
169178
runtimeGlobalName?: string
170179
// we need to know this during codegen to generate proper preambles
171180
prefixIdentifiers?: boolean
181+
bindingMetadata?: BindingMetadata
172182
// generate ssr-specific code?
173183
ssr?: boolean
174184
}

packages/compiler-core/src/transform.ts

+2
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export function createTransformContext(
120120
expressionPlugins = [],
121121
scopeId = null,
122122
ssr = false,
123+
bindingMetadata = {},
123124
onError = defaultOnError
124125
}: TransformOptions
125126
): TransformContext {
@@ -135,6 +136,7 @@ export function createTransformContext(
135136
expressionPlugins,
136137
scopeId,
137138
ssr,
139+
bindingMetadata,
138140
onError,
139141

140142
// state

packages/compiler-core/src/transforms/transformExpression.ts

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// - Parse expressions in templates into compound expressions so that each
22
// identifier gets more accurate source-map locations.
33
//
4-
// - Prefix identifiers with `_ctx.` so that they are accessed from the render
5-
// context
4+
// - Prefix identifiers with `_ctx.` or `$xxx` (for known binding types) so that
5+
// they are accessed from the right source
66
//
77
// - This transform is only applied in non-browser builds because it relies on
88
// an additional JavaScript parser. In the browser, there is no source-map
@@ -25,7 +25,8 @@ import {
2525
import {
2626
isGloballyWhitelisted,
2727
makeMap,
28-
babelParserDefautPlugins
28+
babelParserDefautPlugins,
29+
hasOwn
2930
} from '@vue/shared'
3031
import { createCompilerError, ErrorCodes } from '../errors'
3132
import { Node, Function, Identifier, ObjectProperty } from '@babel/types'
@@ -99,6 +100,14 @@ export function processExpression(
99100
return node
100101
}
101102

103+
const { bindingMetadata } = context
104+
const prefix = (raw: string) => {
105+
const source = hasOwn(bindingMetadata, raw)
106+
? `$` + bindingMetadata[raw]
107+
: `_ctx`
108+
return `${source}.${raw}`
109+
}
110+
102111
// fast path if expression is a simple identifier.
103112
const rawExp = node.content
104113
// bail on parens to prevent any possible function invocations.
@@ -110,7 +119,7 @@ export function processExpression(
110119
!isGloballyWhitelisted(rawExp) &&
111120
!isLiteralWhitelisted(rawExp)
112121
) {
113-
node.content = `_ctx.${rawExp}`
122+
node.content = prefix(rawExp)
114123
} else if (!context.identifiers[rawExp] && !bailConstant) {
115124
// mark node constant for hoisting unless it's referring a scope variable
116125
node.isConstant = true
@@ -148,7 +157,7 @@ export function processExpression(
148157
const isDuplicate = (node: Node & PrefixMeta): boolean =>
149158
ids.some(id => id.start === node.start)
150159

151-
// walk the AST and look for identifiers that need to be prefixed with `_ctx.`.
160+
// walk the AST and look for identifiers that need to be prefixed.
152161
walkJS(ast, {
153162
enter(node: Node & PrefixMeta, parent) {
154163
if (node.type === 'Identifier') {
@@ -160,7 +169,7 @@ export function processExpression(
160169
// rewrite the value
161170
node.prefix = `${node.name}: `
162171
}
163-
node.name = `_ctx.${node.name}`
172+
node.name = prefix(node.name)
164173
ids.push(node)
165174
} else if (!isStaticPropertyKey(node, parent)) {
166175
// The identifier is considered constant unless it's pointing to a

packages/compiler-sfc/src/compileScript.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import MagicString from 'magic-string'
2+
import { BindingMetadata } from '@vue/compiler-core'
23
import { SFCDescriptor, SFCScriptBlock } from './parse'
34
import { parse, ParserPlugin } from '@babel/parser'
45
import { babelParserDefautPlugins, generateCodeFrame } from '@vue/shared'
@@ -28,10 +29,6 @@ export interface SFCScriptCompileOptions {
2829
babelParserPlugins?: ParserPlugin[]
2930
}
3031

31-
export interface BindingMetadata {
32-
[key: string]: 'data' | 'props' | 'setup' | 'ctx'
33-
}
34-
3532
let hasWarned = false
3633

3734
/**

packages/compiler-sfc/src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ export {
2424
SFCAsyncStyleCompileOptions,
2525
SFCStyleCompileResults
2626
} from './compileStyle'
27-
export { SFCScriptCompileOptions, BindingMetadata } from './compileScript'
27+
export { SFCScriptCompileOptions } from './compileScript'
2828
export {
2929
CompilerOptions,
3030
CompilerError,
31+
BindingMetadata,
3132
generateCodeFrame
3233
} from '@vue/compiler-core'

packages/compiler-sfc/src/parse.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@ import {
33
ElementNode,
44
SourceLocation,
55
CompilerError,
6-
TextModes
6+
TextModes,
7+
BindingMetadata
78
} from '@vue/compiler-core'
89
import * as CompilerDOM from '@vue/compiler-dom'
910
import { RawSourceMap, SourceMapGenerator } from 'source-map'
1011
import { generateCodeFrame } from '@vue/shared'
1112
import { TemplateCompiler } from './compileTemplate'
12-
import {
13-
compileScript,
14-
BindingMetadata,
15-
SFCScriptCompileOptions
16-
} from './compileScript'
13+
import { compileScript, SFCScriptCompileOptions } from './compileScript'
1714

1815
export interface SFCParseOptions extends SFCScriptCompileOptions {
1916
filename?: string

packages/runtime-core/src/component.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,8 @@ export interface ComponentInternalOptions {
7777
__file?: string
7878
}
7979

80-
export interface FunctionalComponent<
81-
P = {},
82-
E extends EmitsOptions = {}
83-
> extends ComponentInternalOptions {
80+
export interface FunctionalComponent<P = {}, E extends EmitsOptions = {}>
81+
extends ComponentInternalOptions {
8482
// use of any here is intentional so it can be a valid JSX Element constructor
8583
(props: P, ctx: SetupContext<E>): any
8684
props?: ComponentPropsOptions<P>
@@ -142,7 +140,12 @@ export interface SetupContext<E = ObjectEmitsOptions> {
142140
export type InternalRenderFunction = {
143141
(
144142
ctx: ComponentPublicInstance,
145-
cache: ComponentInternalInstance['renderCache']
143+
cache: ComponentInternalInstance['renderCache'],
144+
// for compiler-optimized bindings
145+
$props: ComponentInternalInstance['props'],
146+
$setup: ComponentInternalInstance['setupState'],
147+
$data: ComponentInternalInstance['data'],
148+
$options: ComponentInternalInstance['ctx']
146149
): VNodeChild
147150
_rc?: boolean // isRuntimeCompiled
148151
}

packages/runtime-core/src/componentOptions.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,12 @@ export interface ComponentOptionsBase<
119119
ctx: any,
120120
push: (item: any) => void,
121121
parentInstance: ComponentInternalInstance,
122-
attrs?: Data
122+
attrs: Data | undefined,
123+
// for compiler-optimized bindings
124+
$props: ComponentInternalInstance['props'],
125+
$setup: ComponentInternalInstance['setupState'],
126+
$data: ComponentInternalInstance['data'],
127+
$options: ComponentInternalInstance['ctx']
123128
) => void
124129

125130
/**

packages/runtime-core/src/componentRenderUtils.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ export function renderComponentRoot(
5050
slots,
5151
attrs,
5252
emit,
53-
renderCache
53+
render,
54+
renderCache,
55+
data,
56+
setupState,
57+
ctx
5458
} = instance
5559

5660
let result
@@ -65,7 +69,15 @@ export function renderComponentRoot(
6569
// runtime-compiled render functions using `with` block.
6670
const proxyToUse = withProxy || proxy
6771
result = normalizeVNode(
68-
instance.render!.call(proxyToUse, proxyToUse!, renderCache)
72+
render!.call(
73+
proxyToUse,
74+
proxyToUse!,
75+
renderCache,
76+
props,
77+
setupState,
78+
data,
79+
ctx
80+
)
6981
)
7082
fallthroughAttrs = attrs
7183
} else {

packages/server-renderer/src/render.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,17 @@ function renderComponentSubTree(
126126

127127
// set current rendering instance for asset resolution
128128
setCurrentRenderingInstance(instance)
129-
comp.ssrRender(instance.proxy, push, instance, attrs)
129+
comp.ssrRender(
130+
instance.proxy,
131+
push,
132+
instance,
133+
attrs,
134+
// compiler-optimized bindings
135+
instance.props,
136+
instance.setupState,
137+
instance.data,
138+
instance.ctx
139+
)
130140
setCurrentRenderingInstance(null)
131141
} else if (instance.render) {
132142
renderVNode(push, renderComponentRoot(instance), instance)

0 commit comments

Comments
 (0)