diff --git a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
index 99b7dd6c0..9bf7229e5 100644
--- a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
+++ b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
@@ -5,7 +5,7 @@ exports[`compiler: integration tests function mode 1`] = `
return function render() {
with (this) {
- const { createVNode: _createVNode, toString: _toString, openBlock: _openBlock, createBlock: _createBlock, Empty: _Empty, Fragment: _Fragment, renderList: _renderList } = _Vue
+ const { toString: _toString, openBlock: _openBlock, createVNode: _createVNode, createBlock: _createBlock, Empty: _Empty, Fragment: _Fragment, renderList: _renderList } = _Vue
return _createVNode(\\"div\\", {
id: \\"foo\\",
@@ -19,7 +19,7 @@ return function render() {
])),
_createVNode(_Fragment, null, _renderList(list, (value, index) => {
return (_openBlock(), _createBlock(\\"div\\", null, [
- _createVNode(\\"span\\", null, _toString(value + index))
+ _createVNode(\\"span\\", null, _toString(value + index), 1 /* TEXT */)
]))
}), 128 /* UNKEYED_FRAGMENT */)
], 2 /* CLASS */)
@@ -28,7 +28,7 @@ return function render() {
`;
exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
-"const { createVNode, toString, openBlock, createBlock, Empty, Fragment, renderList } = Vue
+"const { toString, openBlock, createVNode, createBlock, Empty, Fragment, renderList } = Vue
return function render() {
const _ctx = this
@@ -44,7 +44,7 @@ return function render() {
])),
createVNode(Fragment, null, renderList(_ctx.list, (value, index) => {
return (openBlock(), createBlock(\\"div\\", null, [
- createVNode(\\"span\\", null, toString(value + index))
+ createVNode(\\"span\\", null, toString(value + index), 1 /* TEXT */)
]))
}), 128 /* UNKEYED_FRAGMENT */)
], 2 /* CLASS */)
@@ -52,7 +52,7 @@ return function render() {
`;
exports[`compiler: integration tests module mode 1`] = `
-"import { createVNode, toString, openBlock, createBlock, Empty, Fragment, renderList } from \\"vue\\"
+"import { toString, openBlock, createVNode, createBlock, Empty, Fragment, renderList } from \\"vue\\"
export default function render() {
const _ctx = this
@@ -68,7 +68,7 @@ export default function render() {
])),
createVNode(Fragment, null, renderList(_ctx.list, (value, index) => {
return (openBlock(), createBlock(\\"div\\", null, [
- createVNode(\\"span\\", null, _toString(value + index))
+ createVNode(\\"span\\", null, _toString(value + index), 1 /* TEXT */)
]))
}), 128 /* UNKEYED_FRAGMENT */)
], 2 /* CLASS */)
diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
index 03ab9da4b..d1810ae7a 100644
--- a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
+++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`compiler: transform component slots dynamically named slots 1`] = `
-"const { resolveComponent, createVNode, toString } = Vue
+"const { toString, resolveComponent, createVNode } = Vue
return function render() {
const _ctx = this
@@ -21,7 +21,7 @@ return function render() {
`;
exports[`compiler: transform component slots explicit default slot 1`] = `
-"const { resolveComponent, createVNode, toString } = Vue
+"const { toString, resolveComponent, createVNode } = Vue
return function render() {
const _ctx = this
@@ -37,7 +37,7 @@ return function render() {
`;
exports[`compiler: transform component slots implicit default slot 1`] = `
-"const { resolveComponent, createVNode } = Vue
+"const { createVNode, resolveComponent } = Vue
return function render() {
const _ctx = this
@@ -52,7 +52,7 @@ return function render() {
`;
exports[`compiler: transform component slots named slots 1`] = `
-"const { resolveComponent, createVNode, toString } = Vue
+"const { toString, resolveComponent, createVNode } = Vue
return function render() {
const _ctx = this
@@ -72,12 +72,12 @@ return function render() {
`;
exports[`compiler: transform component slots nested slots scoping 1`] = `
-"const { resolveComponent, createVNode, toString } = Vue
+"const { toString, resolveComponent, createVNode } = Vue
return function render() {
const _ctx = this
- const _component_Comp = resolveComponent(\\"Comp\\")
const _component_Inner = resolveComponent(\\"Inner\\")
+ const _component_Comp = resolveComponent(\\"Comp\\")
return createVNode(_component_Comp, null, {
default: ({ foo }) => [
diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
index 7d2793c9b..f2aed954e 100644
--- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
@@ -26,6 +26,7 @@ import { transformStyle } from '../../src/transforms/transformStyle'
import { transformBind } from '../../src/transforms/vBind'
import { PatchFlags } from '@vue/shared'
import { createObjectMatcher } from '../testUtils'
+import { optimizeText } from '../../src/transforms/optimizeText'
function parseWithElementTransform(
template: string,
@@ -36,7 +37,7 @@ function parseWithElementTransform(
} {
const ast = parse(template, options)
transform(ast, {
- nodeTransforms: [transformElement],
+ nodeTransforms: [optimizeText, transformElement],
...options
})
const codegenNode = (ast.children[0] as ElementNode)
@@ -562,6 +563,20 @@ describe('compiler: element transform', () => {
})
}
+ test('TEXT', () => {
+ const { node } = parseWithBind(`
foo
`)
+ expect(node.arguments.length).toBe(3)
+
+ const { node: node2 } = parseWithBind(`{{ foo }}
`)
+ expect(node2.arguments.length).toBe(4)
+ expect(node2.arguments[3]).toBe(`${PatchFlags.TEXT} /* TEXT */`)
+
+ // multiple nodes, merged with optimze text
+ const { node: node3 } = parseWithBind(`foo {{ bar }} baz
`)
+ expect(node3.arguments.length).toBe(4)
+ expect(node3.arguments[3]).toBe(`${PatchFlags.TEXT} /* TEXT */`)
+ })
+
test('CLASS', () => {
const { node } = parseWithBind(``)
expect(node.arguments.length).toBe(4)
diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts
index 93fc5329c..f2f2ad817 100644
--- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts
@@ -123,9 +123,8 @@ describe('compiler: transform component slots', () => {
one: {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `{ foo }`,
- isStatic: false
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [`{ `, { content: `foo` }, ` }`]
},
returns: [
{
@@ -145,9 +144,8 @@ describe('compiler: transform component slots', () => {
two: {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `{ bar }`,
- isStatic: false
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [`{ `, { content: `bar` }, ` }`]
},
returns: [
{
@@ -186,9 +184,8 @@ describe('compiler: transform component slots', () => {
'[_ctx.one]': {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `{ foo }`,
- isStatic: false
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [`{ `, { content: `foo` }, ` }`]
},
returns: [
{
@@ -208,9 +205,8 @@ describe('compiler: transform component slots', () => {
'[_ctx.two]': {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `{ bar }`,
- isStatic: false
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [`{ `, { content: `bar` }, ` }`]
},
returns: [
{
@@ -249,9 +245,8 @@ describe('compiler: transform component slots', () => {
default: {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params: {
- type: NodeTypes.SIMPLE_EXPRESSION,
- content: `{ foo }`,
- isStatic: false
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [`{ `, { content: `foo` }, ` }`]
},
returns: [
{
diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts
index 26d10f77e..70733b3f8 100644
--- a/packages/compiler-core/src/transforms/transformElement.ts
+++ b/packages/compiler-core/src/transforms/transformElement.ts
@@ -38,65 +38,72 @@ export const transformElement: NodeTransform = (node, context) => {
node.tagType === ElementTypes.ELEMENT ||
node.tagType === ElementTypes.COMPONENT
) {
- const isComponent = node.tagType === ElementTypes.COMPONENT
- let hasProps = node.props.length > 0
- const hasChildren = node.children.length > 0
- let patchFlag: number = 0
- let runtimeDirectives: DirectiveNode[] | undefined
- let dynamicPropNames: string[] | undefined
- let componentIdentifier: string | undefined
+ // perform the work on exit, after all child expressions have been
+ // processed and merged.
+ return () => {
+ const isComponent = node.tagType === ElementTypes.COMPONENT
+ let hasProps = node.props.length > 0
+ const hasChildren = node.children.length > 0
+ let patchFlag: number = 0
+ let runtimeDirectives: DirectiveNode[] | undefined
+ let dynamicPropNames: string[] | undefined
+ let componentIdentifier: string | undefined
- if (isComponent) {
- componentIdentifier = `_component_${toValidId(node.tag)}`
- context.statements.add(
- `const ${componentIdentifier} = ${context.helper(
- RESOLVE_COMPONENT
- )}(${JSON.stringify(node.tag)})`
- )
- }
-
- const args: CallExpression['arguments'] = [
- isComponent ? componentIdentifier! : `"${node.tag}"`
- ]
- // props
- if (hasProps) {
- const propsBuildResult = buildProps(
- node.props,
- node.loc,
- context,
- isComponent
- )
- patchFlag = propsBuildResult.patchFlag
- dynamicPropNames = propsBuildResult.dynamicPropNames
- runtimeDirectives = propsBuildResult.directives
- if (!propsBuildResult.props) {
- hasProps = false
- } else {
- args.push(propsBuildResult.props)
- }
- }
- // children
- if (hasChildren) {
- if (!hasProps) {
- args.push(`null`)
- }
if (isComponent) {
- const { slots, hasDynamicSlotName } = buildSlots(node, context)
- args.push(slots)
- if (hasDynamicSlotName) {
- patchFlag |= PatchFlags.DYNAMIC_SLOTS
+ componentIdentifier = `_component_${toValidId(node.tag)}`
+ context.statements.add(
+ `const ${componentIdentifier} = ${context.helper(
+ RESOLVE_COMPONENT
+ )}(${JSON.stringify(node.tag)})`
+ )
+ }
+
+ const args: CallExpression['arguments'] = [
+ isComponent ? componentIdentifier! : `"${node.tag}"`
+ ]
+ // props
+ if (hasProps) {
+ const propsBuildResult = buildProps(
+ node.props,
+ node.loc,
+ context,
+ isComponent
+ )
+ patchFlag = propsBuildResult.patchFlag
+ dynamicPropNames = propsBuildResult.dynamicPropNames
+ runtimeDirectives = propsBuildResult.directives
+ if (!propsBuildResult.props) {
+ hasProps = false
+ } else {
+ args.push(propsBuildResult.props)
}
- } else {
- if (node.children.length === 1) {
+ }
+ // children
+ if (hasChildren) {
+ if (!hasProps) {
+ args.push(`null`)
+ }
+ if (isComponent) {
+ const { slots, hasDynamicSlotName } = buildSlots(node, context)
+ args.push(slots)
+ if (hasDynamicSlotName) {
+ patchFlag |= PatchFlags.DYNAMIC_SLOTS
+ }
+ } else if (node.children.length === 1) {
const child = node.children[0]
const type = child.type
+ const hasDynamicTextChild =
+ type === NodeTypes.INTERPOLATION ||
+ type === NodeTypes.COMPOUND_EXPRESSION
+ if (hasDynamicTextChild) {
+ patchFlag |= PatchFlags.TEXT
+ }
// pass directly if the only child is one of:
// - text (plain / interpolation / expression)
// - outlet (already an array)
if (
type === NodeTypes.TEXT ||
- type === NodeTypes.INTERPOLATION ||
- type === NodeTypes.COMPOUND_EXPRESSION ||
+ hasDynamicTextChild ||
(type === NodeTypes.ELEMENT &&
(child as ElementNode).tagType === ElementTypes.SLOT)
) {
@@ -108,54 +115,54 @@ export const transformElement: NodeTransform = (node, context) => {
args.push(node.children)
}
}
- }
- // patchFlag & dynamicPropNames
- if (patchFlag !== 0) {
- if (!hasChildren) {
- if (!hasProps) {
+ // patchFlag & dynamicPropNames
+ if (patchFlag !== 0) {
+ if (!hasChildren) {
+ if (!hasProps) {
+ args.push(`null`)
+ }
args.push(`null`)
}
- args.push(`null`)
- }
- if (__DEV__) {
- const flagNames = Object.keys(PatchFlagNames)
- .filter(n => patchFlag & Number(n))
- .map(n => PatchFlagNames[n as any])
- .join(`, `)
- args.push(patchFlag + ` /* ${flagNames} */`)
- } else {
- args.push(patchFlag + '')
- }
- if (dynamicPropNames && dynamicPropNames.length) {
- args.push(
- `[${dynamicPropNames.map(n => JSON.stringify(n)).join(`, `)}]`
- )
- }
- }
-
- const { loc } = node
- const vnode = createCallExpression(
- context.helper(CREATE_VNODE),
- args,
- loc
- )
-
- if (runtimeDirectives && runtimeDirectives.length) {
- node.codegenNode = createCallExpression(
- context.helper(APPLY_DIRECTIVES),
- [
- vnode,
- createArrayExpression(
- runtimeDirectives.map(dir => {
- return createDirectiveArgs(dir, context)
- }),
- loc
+ if (__DEV__) {
+ const flagNames = Object.keys(PatchFlagNames)
+ .filter(n => patchFlag & Number(n))
+ .map(n => PatchFlagNames[n as any])
+ .join(`, `)
+ args.push(patchFlag + ` /* ${flagNames} */`)
+ } else {
+ args.push(patchFlag + '')
+ }
+ if (dynamicPropNames && dynamicPropNames.length) {
+ args.push(
+ `[${dynamicPropNames.map(n => JSON.stringify(n)).join(`, `)}]`
)
- ],
+ }
+ }
+
+ const { loc } = node
+ const vnode = createCallExpression(
+ context.helper(CREATE_VNODE),
+ args,
loc
)
- } else {
- node.codegenNode = vnode
+
+ if (runtimeDirectives && runtimeDirectives.length) {
+ node.codegenNode = createCallExpression(
+ context.helper(APPLY_DIRECTIVES),
+ [
+ vnode,
+ createArrayExpression(
+ runtimeDirectives.map(dir => {
+ return createDirectiveArgs(dir, context)
+ }),
+ loc
+ )
+ ],
+ loc
+ )
+ } else {
+ node.codegenNode = vnode
+ }
}
}
}