Skip to content

Commit 608ec98

Browse files
committed
refactor(compiler): remove resolveNode for better performance
1 parent d4807ea commit 608ec98

File tree

12 files changed

+285
-119
lines changed

12 files changed

+285
-119
lines changed

packages/compiler/src/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ export {
66
} from './compile'
77
export * from './transform'
88

9-
export { resolveDirectiveNode, resolveNode } from './utils'
10-
119
export * from './ir'
1210

1311
export { transformText } from './transforms/transformText'

packages/compiler/src/ir/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,13 @@ export interface SetDynamicEventsIRNode extends BaseIRNode {
124124
event: SimpleExpressionNode
125125
}
126126

127+
export interface SetTextIRNode extends BaseIRNode {
128+
type: IRNodeTypes.SET_TEXT
129+
element: number
130+
values: SimpleExpressionNode[]
131+
generated?: boolean // whether this is a generated empty text node by `processTextLikeContainer`
132+
}
133+
127134
export interface SetNodesIRNode extends BaseIRNode {
128135
type: IRNodeTypes.SET_NODES
129136
element: number
@@ -232,6 +239,7 @@ export type IRNode = OperationNode | RootIRNode
232239
export type OperationNode =
233240
| SetPropIRNode
234241
| SetDynamicPropsIRNode
242+
| SetTextIRNode
235243
| SetNodesIRNode
236244
| SetEventIRNode
237245
| SetDynamicEventsIRNode
Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
1-
import { transformVHtml as _transformVHtml } from '@vue/compiler-vapor'
2-
import { resolveDirectiveNode, resolveNode } from '../utils'
1+
import { createDOMCompilerError, DOMErrorCodes } from '@vue/compiler-dom'
2+
import { IRNodeTypes } from '../ir'
3+
import { resolveExpression, resolveLocation } from '../utils'
34
import type { DirectiveTransform } from '../transform'
5+
import { EMPTY_EXPRESSION } from './utils'
46

57
export const transformVHtml: DirectiveTransform = (dir, node, context) => {
6-
return _transformVHtml(
7-
resolveDirectiveNode(dir, context),
8-
resolveNode(node, context),
9-
context as any,
10-
)
8+
let exp
9+
const loc = resolveLocation(dir.loc, context)
10+
if (!dir.value) {
11+
context.options.onError(
12+
createDOMCompilerError(DOMErrorCodes.X_V_HTML_NO_EXPRESSION, loc),
13+
)
14+
exp = EMPTY_EXPRESSION
15+
} else {
16+
exp = resolveExpression(dir.value, context)
17+
}
18+
if (node.children.length) {
19+
context.options.onError(
20+
createDOMCompilerError(DOMErrorCodes.X_V_HTML_WITH_CHILDREN, loc),
21+
)
22+
context.childrenTemplate.length = 0
23+
}
24+
25+
context.registerEffect([exp], {
26+
type: IRNodeTypes.SET_HTML,
27+
element: context.reference(),
28+
value: exp,
29+
})
1130
}

packages/compiler/src/transforms/vIf.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
type NodeTransform,
1111
type TransformContext,
1212
} from '../transform'
13-
import { isEmptyText, resolveDirectiveNode, resolveLocation } from '../utils'
13+
import { isEmptyText, resolveDirective, resolveLocation } from '../utils'
1414
import { createBranch } from './utils'
1515
import type { JSXAttribute, JSXElement } from '@babel/types'
1616

@@ -26,7 +26,7 @@ export function processIf(
2626
attribute: JSXAttribute,
2727
context: TransformContext,
2828
): (() => void) | undefined {
29-
const dir = resolveDirectiveNode(attribute, context)
29+
const dir = resolveDirective(attribute, context)
3030
if (dir.name !== 'else' && (!dir.exp || !dir.exp.content.trim())) {
3131
const loc = dir.exp ? dir.exp.loc : resolveLocation(node.loc, context)
3232
context.options.onError(
Lines changed: 144 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,148 @@
1-
import { transformVModel as _transformVModel } from '@vue/compiler-vapor'
2-
import { resolveDirectiveNode, resolveNode } from '../utils'
1+
import {
2+
createCompilerError,
3+
createDOMCompilerError,
4+
createSimpleExpression,
5+
DOMErrorCodes,
6+
ErrorCodes,
7+
isMemberExpression,
8+
} from '@vue/compiler-dom'
9+
import { IRNodeTypes, type DirectiveIRNode } from '../ir'
10+
import {
11+
findProp,
12+
getText,
13+
isJSXComponent,
14+
resolveDirective,
15+
resolveLocation,
16+
} from '../utils'
317
import type { DirectiveTransform } from '../transform'
18+
import type { JSXElement } from '@babel/types'
419

5-
export const transformVModel: DirectiveTransform = (dir, node, context) => {
6-
return _transformVModel(
7-
resolveDirectiveNode(dir, context),
8-
resolveNode(node, context),
9-
context as any,
20+
export const transformVModel: DirectiveTransform = (_dir, node, context) => {
21+
const dir = resolveDirective(_dir, context)
22+
const { exp, arg } = dir
23+
if (!exp) {
24+
context.options.onError(
25+
createCompilerError(ErrorCodes.X_V_MODEL_NO_EXPRESSION, dir.loc),
26+
)
27+
return
28+
}
29+
30+
const expString = exp.content
31+
if (!expString.trim() || !isMemberExpression(exp, context.options)) {
32+
context.options.onError(
33+
createCompilerError(ErrorCodes.X_V_MODEL_MALFORMED_EXPRESSION, exp.loc),
34+
)
35+
return
36+
}
37+
38+
const isComponent = isJSXComponent(node)
39+
if (isComponent) {
40+
return {
41+
key: arg ? arg : createSimpleExpression('modelValue', true),
42+
value: exp,
43+
model: true,
44+
modelModifiers: dir.modifiers.map((m) => m.content),
45+
}
46+
}
47+
48+
if (dir.arg)
49+
context.options.onError(
50+
createDOMCompilerError(
51+
DOMErrorCodes.X_V_MODEL_ARG_ON_ELEMENT,
52+
dir.arg.loc,
53+
),
54+
)
55+
const tag = getText(node.openingElement.name, context)
56+
const isCustomElement = context.options.isCustomElement(tag)
57+
let modelType: DirectiveIRNode['modelType'] | undefined = 'text'
58+
// TODO let runtimeDirective: VaporHelper | undefined = 'vModelText'
59+
if (
60+
tag === 'input' ||
61+
tag === 'textarea' ||
62+
tag === 'select' ||
63+
isCustomElement
64+
) {
65+
if (tag === 'input' || isCustomElement) {
66+
const type = findProp(node, 'type')
67+
if (type?.value) {
68+
if (type.value.type === 'JSXExpressionContainer') {
69+
// type={foo}
70+
modelType = 'dynamic'
71+
} else if (type.value.type === 'StringLiteral') {
72+
switch (type.value.value) {
73+
case 'radio':
74+
modelType = 'radio'
75+
break
76+
case 'checkbox':
77+
modelType = 'checkbox'
78+
break
79+
case 'file':
80+
modelType = undefined
81+
context.options.onError(
82+
createDOMCompilerError(
83+
DOMErrorCodes.X_V_MODEL_ON_FILE_INPUT_ELEMENT,
84+
dir.loc,
85+
),
86+
)
87+
break
88+
default:
89+
// text type
90+
checkDuplicatedValue()
91+
break
92+
}
93+
}
94+
} else if (hasDynamicKeyVBind(node)) {
95+
// element has bindings with dynamic keys, which can possibly contain
96+
// "type".
97+
modelType = 'dynamic'
98+
} else {
99+
// text type
100+
checkDuplicatedValue()
101+
}
102+
} else if (tag === 'select') {
103+
modelType = 'select'
104+
} else {
105+
// textarea
106+
checkDuplicatedValue()
107+
}
108+
} else {
109+
context.options.onError(
110+
createDOMCompilerError(
111+
DOMErrorCodes.X_V_MODEL_ON_INVALID_ELEMENT,
112+
dir.loc,
113+
),
114+
)
115+
}
116+
117+
if (modelType)
118+
context.registerOperation({
119+
type: IRNodeTypes.DIRECTIVE,
120+
element: context.reference(),
121+
dir,
122+
name: 'model',
123+
modelType,
124+
builtin: true,
125+
})
126+
127+
function checkDuplicatedValue() {
128+
const value = findProp(node, 'value')
129+
if (value && value.value?.type !== 'StringLiteral') {
130+
context.options.onError(
131+
createDOMCompilerError(
132+
DOMErrorCodes.X_V_MODEL_UNNECESSARY_VALUE,
133+
resolveLocation(value.loc, context),
134+
),
135+
)
136+
}
137+
}
138+
}
139+
140+
function hasDynamicKeyVBind(node: JSXElement): boolean {
141+
return node.openingElement.attributes.some(
142+
(p) =>
143+
p.type === 'JSXSpreadAttribute' ||
144+
(p.type === 'JSXAttribute' &&
145+
p.name.type === 'JSXNamespacedName' &&
146+
!p.name.namespace.name.startsWith('v-')),
10147
)
11148
}
Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
1-
import { transformVShow as _transformVShow } from '@vue/compiler-vapor'
2-
import { resolveDirectiveNode, resolveNode } from '../utils'
1+
import { createDOMCompilerError, DOMErrorCodes } from '@vue/compiler-dom'
2+
import { IRNodeTypes } from '../ir'
3+
import { resolveDirective } from '../utils'
34
import type { DirectiveTransform } from '../transform'
45

5-
export const transformVShow: DirectiveTransform = (dir, node, context) => {
6-
return _transformVShow(
7-
resolveDirectiveNode(dir, context),
8-
resolveNode(node, context),
9-
context as any,
10-
)
6+
export const transformVShow: DirectiveTransform = (_dir, node, context) => {
7+
const dir = resolveDirective(_dir, context)
8+
const { exp, loc } = dir
9+
if (!exp) {
10+
context.options.onError(
11+
createDOMCompilerError(DOMErrorCodes.X_V_SHOW_NO_EXPRESSION, loc),
12+
)
13+
return
14+
}
15+
16+
context.registerOperation({
17+
type: IRNodeTypes.DIRECTIVE,
18+
element: context.reference(),
19+
dir,
20+
name: 'show',
21+
builtin: true,
22+
})
1123
}

packages/compiler/src/transforms/vSlot.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
isEmptyText,
2121
isJSXComponent,
2222
isTemplate,
23-
resolveDirectiveNode,
23+
resolveDirective,
2424
resolveLocation,
2525
resolveSimpleExpressionNode,
2626
} from '../utils'
@@ -36,7 +36,7 @@ export const transformVSlot: NodeTransform = (node, context) => {
3636

3737
const dir = findProp(node, 'v-slot')
3838
const resolvedDirective = dir
39-
? resolveDirectiveNode(dir, context, true)
39+
? resolveDirective(dir, context, true)
4040
: undefined
4141
const { parent } = context
4242

@@ -109,7 +109,7 @@ function transformComponentSlot(
109109
}
110110

111111
const elseIfRE = /^v-else(-if)?$/
112-
// <template #foo>
112+
// <template v-slot:foo>
113113
function transformTemplateSlot(
114114
node: JSXElement,
115115
dir: VaporDirectiveNode,
@@ -134,7 +134,7 @@ function transformTemplateSlot(
134134
registerSlot(slots, arg, block)
135135
}
136136
} else if (vIf) {
137-
const vIfDir = resolveDirectiveNode(vIf, context)
137+
const vIfDir = resolveDirective(vIf, context)
138138
registerDynamicSlot(slots, {
139139
slotType: IRSlotType.CONDITIONAL,
140140
condition: vIfDir.exp!,
@@ -145,7 +145,7 @@ function transformTemplateSlot(
145145
},
146146
})
147147
} else if (vElse) {
148-
const vElseDir = resolveDirectiveNode(vElse, context)
148+
const vElseDir = resolveDirective(vElse, context)
149149
const vIfSlot = slots.at(-1) as IRSlotDynamic
150150
if (vIfSlot.slotType === IRSlotType.CONDITIONAL) {
151151
let ifNode = vIfSlot
Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,52 @@
1-
import { transformVText as _transformVText } from '@vue/compiler-vapor'
2-
import { resolveDirectiveNode, resolveNode } from '../utils'
1+
import { createDOMCompilerError, DOMErrorCodes } from '@vue/compiler-dom'
2+
import { isVoidTag } from '@vue/shared'
3+
import { IRNodeTypes } from '../ir'
4+
import {
5+
getLiteralExpressionValue,
6+
getText,
7+
resolveExpression,
8+
resolveLocation,
9+
} from '../utils'
310
import type { DirectiveTransform } from '../transform'
11+
import { EMPTY_EXPRESSION } from './utils'
412

513
export const transformVText: DirectiveTransform = (dir, node, context) => {
6-
return _transformVText(
7-
resolveDirectiveNode(dir, context),
8-
resolveNode(node, context),
9-
context as any,
10-
)
14+
let exp
15+
const loc = resolveLocation(dir.loc, context)
16+
if (!dir.value) {
17+
context.options.onError(
18+
createDOMCompilerError(DOMErrorCodes.X_V_TEXT_NO_EXPRESSION, loc),
19+
)
20+
exp = EMPTY_EXPRESSION
21+
} else {
22+
exp = resolveExpression(dir.value, context)
23+
}
24+
if (node.children.length) {
25+
context.options.onError(
26+
createDOMCompilerError(DOMErrorCodes.X_V_TEXT_WITH_CHILDREN, loc),
27+
)
28+
context.childrenTemplate.length = 0
29+
}
30+
31+
// v-text on void tags do nothing
32+
if (isVoidTag(getText(node.openingElement.name, context))) {
33+
return
34+
}
35+
36+
const literal = getLiteralExpressionValue(exp)
37+
if (literal != null) {
38+
context.childrenTemplate = [String(literal)]
39+
} else {
40+
context.childrenTemplate = [' ']
41+
context.registerOperation({
42+
type: IRNodeTypes.GET_TEXT_CHILD,
43+
parent: context.reference(),
44+
})
45+
context.registerEffect([exp], {
46+
type: IRNodeTypes.SET_TEXT,
47+
element: context.reference(),
48+
values: [exp],
49+
generated: true,
50+
})
51+
}
1152
}

0 commit comments

Comments
 (0)