From 7d1586246434412369f50944e5ee05c3ca7e27a1 Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Mon, 1 Mar 2021 21:37:21 +0800 Subject: [PATCH 01/15] wip: initial work for vnode creation --- .../compiler-core/__tests__/codegen.spec.ts | 27 +- .../compiler-core/__tests__/scopeId.spec.ts | 12 +- packages/compiler-core/__tests__/testUtils.ts | 20 +- .../__tests__/transforms/hoistStatic.spec.ts | 19 +- .../transforms/transformElement.spec.ts | 208 ++++++++++----- .../__tests__/transforms/vIf.spec.ts | 47 +++- packages/compiler-core/src/ast.ts | 32 ++- packages/compiler-core/src/codegen.ts | 40 ++- packages/compiler-core/src/options.ts | 9 + packages/compiler-core/src/runtimeHelpers.ts | 28 ++ packages/compiler-core/src/transform.ts | 36 ++- .../src/transforms/hoistStatic.ts | 70 ++++- .../src/transforms/transformElement.ts | 235 ++++++++++++++++- packages/compiler-core/src/transforms/vFor.ts | 43 +++- packages/compiler-core/src/transforms/vIf.ts | 36 ++- packages/compiler-core/src/utils.ts | 86 ++++++- .../__tests__/compileScript.spec.ts | 6 +- packages/compiler-ssr/src/index.ts | 1 + .../src/transforms/ssrTransformComponent.ts | 1 + .../__tests__/rendererOptimizedMode.spec.ts | 43 +++- packages/runtime-core/src/index.ts | 14 +- packages/runtime-core/src/vnode.ts | 239 +++++++++++++++++- packages/shared/src/normalizeProp.ts | 29 +++ packages/shared/src/shapeFlags.ts | 17 ++ 24 files changed, 1132 insertions(+), 166 deletions(-) diff --git a/packages/compiler-core/__tests__/codegen.spec.ts b/packages/compiler-core/__tests__/codegen.spec.ts index 75001f94aff..61908a9105b 100644 --- a/packages/compiler-core/__tests__/codegen.spec.ts +++ b/packages/compiler-core/__tests__/codegen.spec.ts @@ -31,7 +31,8 @@ import { RESOLVE_COMPONENT, CREATE_COMMENT, FRAGMENT, - RENDER_LIST + RENDER_LIST, + CREATE_ELEMENT_VNODE } from '../src/runtimeHelpers' import { createElementWithCodegen, genFlagText } from './testUtils' import { PatchFlags } from '@vue/shared' @@ -395,12 +396,12 @@ describe('compiler: codegen', () => { }) ) expect(code).toMatch(` - return _${helperNameMap[CREATE_VNODE]}("div", { + return _${helperNameMap[CREATE_ELEMENT_VNODE]}("div", { id: "foo", [prop]: bar, [foo + bar]: bar }, [ - _${helperNameMap[CREATE_VNODE]}("p", { "some-key": "foo" }) + _${helperNameMap[CREATE_ELEMENT_VNODE]}("p", { "some-key": "foo" }) ], ${PatchFlags.FULL_PROPS})`) expect(code).toMatchSnapshot() }) @@ -658,11 +659,11 @@ describe('compiler: codegen', () => { test('tag only', () => { expect(genCode(createVNodeCall(null, `"div"`))).toMatchInlineSnapshot(` - "return _createVNode(\\"div\\") + "return _createElementVNode(\\"div\\") " `) expect(genCode(createVNodeCall(null, FRAGMENT))).toMatchInlineSnapshot(` - "return _createVNode(_Fragment) + "return _createElementVNode(_Fragment) " `) }) @@ -670,7 +671,7 @@ describe('compiler: codegen', () => { test('with props', () => { expect(genCode(createVNodeCall(null, `"div"`, mockProps))) .toMatchInlineSnapshot(` - "return _createVNode(\\"div\\", { foo: \\"bar\\" }) + "return _createElementVNode(\\"div\\", { foo: \\"bar\\" }) " `) }) @@ -678,7 +679,7 @@ describe('compiler: codegen', () => { test('with children, no props', () => { expect(genCode(createVNodeCall(null, `"div"`, undefined, mockChildren))) .toMatchInlineSnapshot(` - "return _createVNode(\\"div\\", null, children) + "return _createElementVNode(\\"div\\", null, children) " `) }) @@ -686,7 +687,7 @@ describe('compiler: codegen', () => { test('with children + props', () => { expect(genCode(createVNodeCall(null, `"div"`, mockProps, mockChildren))) .toMatchInlineSnapshot(` - "return _createVNode(\\"div\\", { foo: \\"bar\\" }, children) + "return _createElementVNode(\\"div\\", { foo: \\"bar\\" }, children) " `) }) @@ -694,7 +695,7 @@ describe('compiler: codegen', () => { test('with patchFlag and no children/props', () => { expect(genCode(createVNodeCall(null, `"div"`, undefined, undefined, '1'))) .toMatchInlineSnapshot(` - "return _createVNode(\\"div\\", null, null, 1) + "return _createElementVNode(\\"div\\", null, null, 1) " `) }) @@ -714,7 +715,7 @@ describe('compiler: codegen', () => { ) ) ).toMatchInlineSnapshot(` - "return (_openBlock(), _createBlock(\\"div\\", { foo: \\"bar\\" }, children)) + "return (_openBlock(), _createElementBlock(\\"div\\", { foo: \\"bar\\" }, children)) " `) }) @@ -735,7 +736,7 @@ describe('compiler: codegen', () => { ) ) ).toMatchInlineSnapshot(` - "return (_openBlock(true), _createBlock(\\"div\\", { foo: \\"bar\\" }, children)) + "return (_openBlock(true), _createElementBlock(\\"div\\", { foo: \\"bar\\" }, children)) " `) }) @@ -754,7 +755,7 @@ describe('compiler: codegen', () => { ) ) ).toMatchInlineSnapshot(` - "return _withDirectives(_createVNode(\\"div\\", { foo: \\"bar\\" }, children), [ + "return _withDirectives(_createElementVNode(\\"div\\", { foo: \\"bar\\" }, children), [ [foo, bar] ]) " @@ -776,7 +777,7 @@ describe('compiler: codegen', () => { ) ) ).toMatchInlineSnapshot(` - "return _withDirectives((_openBlock(), _createBlock(\\"div\\", { foo: \\"bar\\" }, children)), [ + "return _withDirectives((_openBlock(), _createElementBlock(\\"div\\", { foo: \\"bar\\" }, children)), [ [foo, bar] ]) " diff --git a/packages/compiler-core/__tests__/scopeId.spec.ts b/packages/compiler-core/__tests__/scopeId.spec.ts index 37ffb8893ca..5f8cef3e77e 100644 --- a/packages/compiler-core/__tests__/scopeId.spec.ts +++ b/packages/compiler-core/__tests__/scopeId.spec.ts @@ -1,6 +1,6 @@ import { baseCompile } from '../src/compile' import { PUSH_SCOPE_ID, POP_SCOPE_ID } from '../src/runtimeHelpers' -import { PatchFlags } from '@vue/shared' +import { PatchFlags, ShapeFlagNames, ShapeFlags } from '@vue/shared' import { genFlagText } from './testUtils' /** @@ -73,11 +73,17 @@ describe('scopeId compiler support', () => { expect(code).toMatch( [ `_pushScopeId("test")`, - `const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "hello", ${genFlagText( + `const _hoisted_1 = /*#__PURE__*/_createElementVNode("div", null, "hello", ${genFlagText( PatchFlags.HOISTED + )}, null, ${genFlagText( + [ShapeFlags.ELEMENT, ShapeFlags.TEXT_CHILDREN], + ShapeFlagNames )})`, - `const _hoisted_2 = /*#__PURE__*/_createVNode("div", null, "world", ${genFlagText( + `const _hoisted_2 = /*#__PURE__*/_createElementVNode("div", null, "world", ${genFlagText( PatchFlags.HOISTED + )}, null, ${genFlagText( + [ShapeFlags.ELEMENT, ShapeFlags.TEXT_CHILDREN], + ShapeFlagNames )})`, `_popScopeId()` ].join('\n') diff --git a/packages/compiler-core/__tests__/testUtils.ts b/packages/compiler-core/__tests__/testUtils.ts index 61d2d8d0fce..2761aba698a 100644 --- a/packages/compiler-core/__tests__/testUtils.ts +++ b/packages/compiler-core/__tests__/testUtils.ts @@ -6,7 +6,13 @@ import { ElementTypes, VNodeCall } from '../src' -import { isString, PatchFlags, PatchFlagNames, isArray } from '@vue/shared' +import { + isString, + PatchFlags, + PatchFlagNames, + isArray, + ShapeFlags +} from '@vue/shared' const leadingBracketRE = /^\[/ const bracketsRE = /^\[|\]$/g @@ -63,19 +69,25 @@ export function createElementWithCodegen( directives: undefined, isBlock: false, disableTracking: false, + isComponent: false, + shapeFlag: ShapeFlags.ELEMENT + ``, loc: locStub } } } -export function genFlagText(flag: PatchFlags | PatchFlags[]) { +type Flags = PatchFlags | ShapeFlags +export function genFlagText( + flag: Flags | Flags[], + names: { [k: number]: string } = PatchFlagNames +) { if (isArray(flag)) { let f = 0 flag.forEach(ff => { f |= ff }) - return `${f} /* ${flag.map(f => PatchFlagNames[f]).join(', ')} */` + return `${f} /* ${flag.map(f => names[f]).join(', ')} */` } else { - return `${flag} /* ${PatchFlagNames[flag]} */` + return `${flag} /* ${names[flag]} */` } } diff --git a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts index 37e91628479..a91635ed687 100644 --- a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts +++ b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts @@ -10,7 +10,12 @@ import { ForNode, ConstantTypes } from '../../src' -import { FRAGMENT, RENDER_LIST, CREATE_TEXT } from '../../src/runtimeHelpers' +import { + FRAGMENT, + RENDER_LIST, + CREATE_TEXT, + NORMALIZE_CLASS +} from '../../src/runtimeHelpers' import { transformElement } from '../../src/transforms/transformElement' import { transformExpression } from '../../src/transforms/transformExpression' import { transformIf } from '../../src/transforms/vIf' @@ -510,9 +515,15 @@ describe('compiler: hoistStatic transform', () => { constType: ConstantTypes.CAN_STRINGIFY }, value: { - content: `{ foo: true }`, - isStatic: false, - constType: ConstantTypes.CAN_STRINGIFY + type: NodeTypes.JS_CALL_EXPRESSION, + callee: NORMALIZE_CLASS, + arguments: [ + { + content: `{ foo: true }`, + isStatic: false, + constType: ConstantTypes.CAN_STRINGIFY + } + ] } } ] diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts index 33fdb7dba19..a5c5e866085 100644 --- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts @@ -17,7 +17,12 @@ import { RESOLVE_DYNAMIC_COMPONENT, SUSPENSE, KEEP_ALIVE, - BASE_TRANSITION + BASE_TRANSITION, + NORMALIZE_CLASS, + NORMALIZE_STYLE, + NORMALIZE_PROPS, + NORMALIZE_PROPS_FOR_STYLE, + GUARD_REACTIVE_PROPS } from '../../src/runtimeHelpers' import { NodeTypes, @@ -158,11 +163,25 @@ describe('compiler: element transform', () => { const { root, node } = parseWithElementTransform(`
`) // single v-bind doesn't need mergeProps expect(root.helpers).not.toContain(MERGE_PROPS) + expect(root.helpers).toContain(NORMALIZE_PROPS) + expect(root.helpers).toContain(GUARD_REACTIVE_PROPS) // should directly use `obj` in props position expect(node.props).toMatchObject({ - type: NodeTypes.SIMPLE_EXPRESSION, - content: `obj` + type: NodeTypes.JS_CALL_EXPRESSION, + callee: NORMALIZE_PROPS, + arguments: [ + { + type: NodeTypes.JS_CALL_EXPRESSION, + callee: GUARD_REACTIVE_PROPS, + arguments: [ + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `obj` + } + ] + } + ] }) }) @@ -171,17 +190,24 @@ describe('compiler: element transform', () => { `
` ) expect(root.helpers).toContain(MERGE_PROPS) + expect(root.helpers).toContain(NORMALIZE_PROPS) expect(node.props).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: NORMALIZE_PROPS, arguments: [ - createObjectMatcher({ - id: 'foo' - }), { - type: NodeTypes.SIMPLE_EXPRESSION, - content: `obj` + type: NodeTypes.JS_CALL_EXPRESSION, + callee: MERGE_PROPS, + arguments: [ + createObjectMatcher({ + id: 'foo' + }), + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `obj` + } + ] } ] }) @@ -192,18 +218,25 @@ describe('compiler: element transform', () => { `
` ) expect(root.helpers).toContain(MERGE_PROPS) + expect(root.helpers).toContain(NORMALIZE_PROPS) expect(node.props).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: NORMALIZE_PROPS, arguments: [ { - type: NodeTypes.SIMPLE_EXPRESSION, - content: `obj` - }, - createObjectMatcher({ - id: 'foo' - }) + type: NodeTypes.JS_CALL_EXPRESSION, + callee: MERGE_PROPS, + arguments: [ + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `obj` + }, + createObjectMatcher({ + id: 'foo' + }) + ] + } ] }) }) @@ -213,21 +246,28 @@ describe('compiler: element transform', () => { `
` ) expect(root.helpers).toContain(MERGE_PROPS) + expect(root.helpers).toContain(NORMALIZE_PROPS_FOR_STYLE) expect(node.props).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: NORMALIZE_PROPS_FOR_STYLE, arguments: [ - createObjectMatcher({ - id: 'foo' - }), { - type: NodeTypes.SIMPLE_EXPRESSION, - content: `obj` - }, - createObjectMatcher({ - class: 'bar' - }) + type: NodeTypes.JS_CALL_EXPRESSION, + callee: MERGE_PROPS, + arguments: [ + createObjectMatcher({ + id: 'foo' + }), + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `obj` + }, + createObjectMatcher({ + class: 'bar' + }) + ] + } ] }) }) @@ -237,27 +277,34 @@ describe('compiler: element transform', () => { `
` ) expect(root.helpers).toContain(MERGE_PROPS) + expect(root.helpers).toContain(NORMALIZE_PROPS_FOR_STYLE) expect(node.props).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: NORMALIZE_PROPS_FOR_STYLE, arguments: [ - createObjectMatcher({ - id: 'foo' - }), { type: NodeTypes.JS_CALL_EXPRESSION, - callee: TO_HANDLERS, + callee: MERGE_PROPS, arguments: [ + createObjectMatcher({ + id: 'foo' + }), { - type: NodeTypes.SIMPLE_EXPRESSION, - content: `obj` - } + type: NodeTypes.JS_CALL_EXPRESSION, + callee: TO_HANDLERS, + arguments: [ + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `obj` + } + ] + }, + createObjectMatcher({ + class: 'bar' + }) ] - }, - createObjectMatcher({ - class: 'bar' - }) + } ] }) }) @@ -267,27 +314,34 @@ describe('compiler: element transform', () => { `
` ) expect(root.helpers).toContain(MERGE_PROPS) + expect(root.helpers).toContain(NORMALIZE_PROPS) expect(node.props).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: NORMALIZE_PROPS, arguments: [ - createObjectMatcher({ - id: 'foo' - }), { type: NodeTypes.JS_CALL_EXPRESSION, - callee: TO_HANDLERS, + callee: MERGE_PROPS, arguments: [ + createObjectMatcher({ + id: 'foo' + }), + { + type: NodeTypes.JS_CALL_EXPRESSION, + callee: TO_HANDLERS, + arguments: [ + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `handlers` + } + ] + }, { type: NodeTypes.SIMPLE_EXPRESSION, - content: `handlers` + content: `obj` } ] - }, - { - type: NodeTypes.SIMPLE_EXPRESSION, - content: `obj` } ] }) @@ -654,7 +708,7 @@ describe('compiler: element transform', () => { }) test(`props merging: style`, () => { - const { node } = parseWithElementTransform( + const { node, root } = parseWithElementTransform( `
`, { nodeTransforms: [transformStyle, transformElement], @@ -663,6 +717,7 @@ describe('compiler: element transform', () => { } } ) + expect(root.helpers).toContain(NORMALIZE_STYLE) expect(node.props).toMatchObject({ type: NodeTypes.JS_OBJECT_EXPRESSION, properties: [ @@ -674,17 +729,23 @@ describe('compiler: element transform', () => { isStatic: true }, value: { - type: NodeTypes.JS_ARRAY_EXPRESSION, - elements: [ + type: NodeTypes.JS_CALL_EXPRESSION, + callee: NORMALIZE_STYLE, + arguments: [ { - type: NodeTypes.SIMPLE_EXPRESSION, - content: `{"color":"green"}`, - isStatic: false - }, - { - type: NodeTypes.SIMPLE_EXPRESSION, - content: `{ color: 'red' }`, - isStatic: false + type: NodeTypes.JS_ARRAY_EXPRESSION, + elements: [ + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `{"color":"green"}`, + isStatic: false + }, + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `{ color: 'red' }`, + isStatic: false + } + ] } ] } @@ -694,7 +755,7 @@ describe('compiler: element transform', () => { }) test(`props merging: class`, () => { - const { node } = parseWithElementTransform( + const { node, root } = parseWithElementTransform( `
`, { directiveTransforms: { @@ -702,6 +763,7 @@ describe('compiler: element transform', () => { } } ) + expect(root.helpers).toContain(NORMALIZE_CLASS) expect(node.props).toMatchObject({ type: NodeTypes.JS_OBJECT_EXPRESSION, properties: [ @@ -713,17 +775,23 @@ describe('compiler: element transform', () => { isStatic: true }, value: { - type: NodeTypes.JS_ARRAY_EXPRESSION, - elements: [ - { - type: NodeTypes.SIMPLE_EXPRESSION, - content: `foo`, - isStatic: true - }, + type: NodeTypes.JS_CALL_EXPRESSION, + callee: NORMALIZE_CLASS, + arguments: [ { - type: NodeTypes.SIMPLE_EXPRESSION, - content: `{ bar: isBar }`, - isStatic: false + type: NodeTypes.JS_ARRAY_EXPRESSION, + elements: [ + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `foo`, + isStatic: true + }, + { + type: NodeTypes.SIMPLE_EXPRESSION, + content: `{ bar: isBar }`, + isStatic: false + } + ] } ] } diff --git a/packages/compiler-core/__tests__/transforms/vIf.spec.ts b/packages/compiler-core/__tests__/transforms/vIf.spec.ts index f1b91cc4567..5d0371a738a 100644 --- a/packages/compiler-core/__tests__/transforms/vIf.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vIf.spec.ts @@ -22,6 +22,7 @@ import { CREATE_COMMENT, FRAGMENT, MERGE_PROPS, + NORMALIZE_PROPS, RENDER_SLOT } from '../../src/runtimeHelpers' import { createObjectMatcher } from '../testUtils' @@ -556,8 +557,14 @@ describe('compiler: v-if', () => { const branch1 = codegenNode.consequent as VNodeCall expect(branch1.props).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, - arguments: [createObjectMatcher({ key: `[0]` }), { content: `obj` }] + callee: NORMALIZE_PROPS, + arguments: [ + { + type: NodeTypes.JS_CALL_EXPRESSION, + callee: MERGE_PROPS, + arguments: [createObjectMatcher({ key: `[0]` }), { content: `obj` }] + } + ] }) }) @@ -568,13 +575,19 @@ describe('compiler: v-if', () => { const branch1 = codegenNode.consequent as VNodeCall expect(branch1.props).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: NORMALIZE_PROPS, arguments: [ - createObjectMatcher({ - key: '[0]', - id: 'foo' - }), - { content: `obj` } + { + type: NodeTypes.JS_CALL_EXPRESSION, + callee: MERGE_PROPS, + arguments: [ + createObjectMatcher({ + key: '[0]', + id: 'foo' + }), + { content: `obj` } + ] + } ] }) }) @@ -586,13 +599,19 @@ describe('compiler: v-if', () => { const branch1 = codegenNode.consequent as VNodeCall expect(branch1.props).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: NORMALIZE_PROPS, arguments: [ - createObjectMatcher({ key: `[0]` }), - { content: `obj` }, - createObjectMatcher({ - id: 'foo' - }) + { + type: NodeTypes.JS_CALL_EXPRESSION, + callee: MERGE_PROPS, + arguments: [ + createObjectMatcher({ key: `[0]` }), + { content: `obj` }, + createObjectMatcher({ + id: 'foo' + }) + ] + } ] }) }) diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index df843520209..7e97919f419 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -5,10 +5,14 @@ import { CREATE_SLOTS, RENDER_LIST, OPEN_BLOCK, - CREATE_BLOCK, FRAGMENT, - CREATE_VNODE, - WITH_DIRECTIVES + WITH_DIRECTIVES, + CREATE_COMPONENT_VNODE, + CREATE_ELEMENT_VNODE, + CREATE_COMPONENT_BLOCK, + CREATE_ELEMENT_BLOCK, + CREATE_BLOCK, + CREATE_VNODE } from './runtimeHelpers' import { PropsExpression } from './transforms/transformElement' import { ImportItem, TransformContext } from './transform' @@ -293,6 +297,8 @@ export interface VNodeCall extends Node { directives: DirectiveArguments | undefined isBlock: boolean disableTracking: boolean + isComponent: boolean + shapeFlag: string | undefined } // JS Node Types --------------------------------------------------------------- @@ -560,14 +566,28 @@ export function createVNodeCall( directives?: VNodeCall['directives'], isBlock: VNodeCall['isBlock'] = false, disableTracking: VNodeCall['disableTracking'] = false, + isComponent: VNodeCall['isComponent'] = false, + shapeFlag: VNodeCall['shapeFlag'] = undefined, loc = locStub ): VNodeCall { if (context) { if (isBlock) { context.helper(OPEN_BLOCK) - context.helper(CREATE_BLOCK) + context.helper( + context.forSSR + ? CREATE_BLOCK + : isComponent + ? CREATE_COMPONENT_BLOCK + : CREATE_ELEMENT_BLOCK + ) } else { - context.helper(CREATE_VNODE) + context.helper( + context.forSSR + ? CREATE_VNODE + : isComponent + ? CREATE_COMPONENT_VNODE + : CREATE_ELEMENT_VNODE + ) } if (directives) { context.helper(WITH_DIRECTIVES) @@ -584,6 +604,8 @@ export function createVNodeCall( directives, isBlock, disableTracking, + isComponent, + shapeFlag, loc } } diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 6367743d8bc..c74891ea1d9 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -47,11 +47,15 @@ import { POP_SCOPE_ID, WITH_SCOPE_ID, WITH_DIRECTIVES, - CREATE_BLOCK, + CREATE_ELEMENT_BLOCK, + CREATE_COMPONENT_BLOCK, + CREATE_ELEMENT_VNODE, + CREATE_COMPONENT_VNODE, OPEN_BLOCK, CREATE_STATIC, WITH_CTX, - RESOLVE_FILTER + RESOLVE_FILTER, + CREATE_BLOCK } from './runtimeHelpers' import { ImportItem } from './transform' @@ -98,7 +102,8 @@ function createCodegenContext( optimizeImports = false, runtimeGlobalName = `Vue`, runtimeModuleName = `vue`, - ssr = false + ssr = false, + forSSR = false }: CodegenOptions ): CodegenContext { const context: CodegenContext = { @@ -111,6 +116,7 @@ function createCodegenContext( runtimeGlobalName, runtimeModuleName, ssr, + forSSR, source: ast.loc.source, code: ``, column: 1, @@ -219,7 +225,6 @@ export function generate( } else { genFunctionPreamble(ast, preambleContext) } - // enter render function const functionName = ssr ? `ssrRender` : `render` const args = ssr ? ['_ctx', '_push', '_parent', '_attrs'] : ['_ctx', '_cache'] @@ -356,6 +361,8 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) { if (ast.hoists.length) { const staticHelpers = [ CREATE_VNODE, + CREATE_ELEMENT_VNODE, + CREATE_COMPONENT_VNODE, CREATE_COMMENT, CREATE_TEXT, CREATE_STATIC @@ -752,10 +759,12 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) { props, children, patchFlag, + shapeFlag, dynamicProps, directives, isBlock, - disableTracking + disableTracking, + isComponent } = node if (directives) { push(helper(WITH_DIRECTIVES) + `(`) @@ -766,9 +775,26 @@ function genVNodeCall(node: VNodeCall, context: CodegenContext) { if (pure) { push(PURE_ANNOTATION) } - push(helper(isBlock ? CREATE_BLOCK : CREATE_VNODE) + `(`, node) + let callHelper: symbol = isBlock ? CREATE_BLOCK : CREATE_VNODE + if (!context.ssr) { + callHelper = isBlock + ? isComponent + ? CREATE_COMPONENT_BLOCK + : CREATE_ELEMENT_BLOCK + : isComponent + ? CREATE_COMPONENT_VNODE + : CREATE_ELEMENT_VNODE + } + push(helper(callHelper) + `(`, node) genNodeList( - genNullableArgs([tag, props, children, patchFlag, dynamicProps]), + genNullableArgs([ + tag, + props, + children, + patchFlag, + dynamicProps, + context.ssr ? null : shapeFlag + ]), context ) push(`)`) diff --git a/packages/compiler-core/src/options.ts b/packages/compiler-core/src/options.ts index a387c167875..b4a51dd730d 100644 --- a/packages/compiler-core/src/options.ts +++ b/packages/compiler-core/src/options.ts @@ -125,8 +125,17 @@ interface SharedTransformCodegenOptions { * Generate SSR-optimized render functions instead. * The resulting function must be attached to the component via the * `ssrRender` option instead of `render`. + * Note: it is used to indicate the runtime environment of the generated code */ ssr?: boolean + /** + * Indicates whether the compiler generates code for SSR, + * when compiler generates code for the fallback branch of SSR: + * - context.ssr = false + * - context.forSSR = true + * see `subTransform` in `ssrTransformCompoent.ts` + */ + forSSR?: boolean /** * Optional binding metadata analyzed from script - used to optimize * binding access when `prefixIdentifiers` is enabled. diff --git a/packages/compiler-core/src/runtimeHelpers.ts b/packages/compiler-core/src/runtimeHelpers.ts index 94ce1ebc990..269e270f8de 100644 --- a/packages/compiler-core/src/runtimeHelpers.ts +++ b/packages/compiler-core/src/runtimeHelpers.ts @@ -5,7 +5,15 @@ export const KEEP_ALIVE = Symbol(__DEV__ ? `KeepAlive` : ``) export const BASE_TRANSITION = Symbol(__DEV__ ? `BaseTransition` : ``) export const OPEN_BLOCK = Symbol(__DEV__ ? `openBlock` : ``) export const CREATE_BLOCK = Symbol(__DEV__ ? `createBlock` : ``) +export const CREATE_ELEMENT_BLOCK = Symbol(__DEV__ ? `createElementBlock` : ``) +export const CREATE_COMPONENT_BLOCK = Symbol( + __DEV__ ? `createComponentBlock` : `` +) export const CREATE_VNODE = Symbol(__DEV__ ? `createVNode` : ``) +export const CREATE_ELEMENT_VNODE = Symbol(__DEV__ ? `createElementVNode` : ``) +export const CREATE_COMPONENT_VNODE = Symbol( + __DEV__ ? `createComponentVNode` : `` +) export const CREATE_COMMENT = Symbol(__DEV__ ? `createCommentVNode` : ``) export const CREATE_TEXT = Symbol(__DEV__ ? `createTextVNode` : ``) export const CREATE_STATIC = Symbol(__DEV__ ? `createStaticVNode` : ``) @@ -21,6 +29,16 @@ export const RENDER_SLOT = Symbol(__DEV__ ? `renderSlot` : ``) export const CREATE_SLOTS = Symbol(__DEV__ ? `createSlots` : ``) export const TO_DISPLAY_STRING = Symbol(__DEV__ ? `toDisplayString` : ``) export const MERGE_PROPS = Symbol(__DEV__ ? `mergeProps` : ``) +export const NORMALIZE_CLASS = Symbol(__DEV__ ? `normalizeClass` : ``) +export const NORMALIZE_STYLE = Symbol(__DEV__ ? `normalizeStyle` : ``) +export const NORMALIZE_PROPS = Symbol(__DEV__ ? `normalizeProps` : ``) +export const NORMALIZE_PROPS_FOR_CLASS = Symbol( + __DEV__ ? `normalizePropsForClass` : `` +) +export const NORMALIZE_PROPS_FOR_STYLE = Symbol( + __DEV__ ? `normalizePropsForStyle` : `` +) +export const GUARD_REACTIVE_PROPS = Symbol(__DEV__ ? `guardReactiveProps` : ``) export const TO_HANDLERS = Symbol(__DEV__ ? `toHandlers` : ``) export const CAMELIZE = Symbol(__DEV__ ? `camelize` : ``) export const CAPITALIZE = Symbol(__DEV__ ? `capitalize` : ``) @@ -44,7 +62,11 @@ export const helperNameMap: any = { [BASE_TRANSITION]: `BaseTransition`, [OPEN_BLOCK]: `openBlock`, [CREATE_BLOCK]: `createBlock`, + [CREATE_ELEMENT_BLOCK]: `createElementBlock`, + [CREATE_COMPONENT_BLOCK]: `createComponentBlock`, [CREATE_VNODE]: `createVNode`, + [CREATE_ELEMENT_VNODE]: `createElementVNode`, + [CREATE_COMPONENT_VNODE]: `createComponentVNode`, [CREATE_COMMENT]: `createCommentVNode`, [CREATE_TEXT]: `createTextVNode`, [CREATE_STATIC]: `createStaticVNode`, @@ -58,6 +80,12 @@ export const helperNameMap: any = { [CREATE_SLOTS]: `createSlots`, [TO_DISPLAY_STRING]: `toDisplayString`, [MERGE_PROPS]: `mergeProps`, + [NORMALIZE_CLASS]: `normalizeClass`, + [NORMALIZE_STYLE]: `normalizeStyle`, + [NORMALIZE_PROPS]: `normalizeProps`, + [NORMALIZE_PROPS_FOR_CLASS]: `normalizePropsForClass`, + [NORMALIZE_PROPS_FOR_STYLE]: `normalizePropsForStyle`, + [GUARD_REACTIVE_PROPS]: `guardReactiveProps`, [TO_HANDLERS]: `toHandlers`, [CAMELIZE]: `camelize`, [CAPITALIZE]: `capitalize`, diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index add7ea13c4b..98d4238fa40 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -26,17 +26,23 @@ import { PatchFlagNames, EMPTY_OBJ, capitalize, - camelize + camelize, + ShapeFlags, + ShapeFlagNames } from '@vue/shared' import { defaultOnError, defaultOnWarn } from './errors' import { TO_DISPLAY_STRING, FRAGMENT, helperNameMap, - CREATE_BLOCK, CREATE_COMMENT, OPEN_BLOCK, - CREATE_VNODE + CREATE_ELEMENT_BLOCK, + CREATE_COMPONENT_BLOCK, + CREATE_BLOCK, + CREATE_VNODE, + CREATE_COMPONENT_VNODE, + CREATE_ELEMENT_VNODE } from './runtimeHelpers' import { isVSlot } from './utils' import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic' @@ -139,6 +145,7 @@ export function createTransformContext( scopeId = null, slotted = true, ssr = false, + forSSR = false, ssrCssVars = ``, bindingMetadata = EMPTY_OBJ, inline = false, @@ -164,6 +171,7 @@ export function createTransformContext( scopeId, slotted, ssr, + forSSR, ssrCssVars, bindingMetadata, inline, @@ -346,10 +354,22 @@ function createRootCodegen(root: RootNode, context: TransformContext) { const codegenNode = child.codegenNode if (codegenNode.type === NodeTypes.VNODE_CALL) { if (!codegenNode.isBlock) { - removeHelper(CREATE_VNODE) codegenNode.isBlock = true + removeHelper( + context.ssr + ? CREATE_VNODE + : codegenNode.isComponent + ? CREATE_COMPONENT_VNODE + : CREATE_ELEMENT_VNODE + ) helper(OPEN_BLOCK) - helper(CREATE_BLOCK) + helper( + context.ssr + ? CREATE_BLOCK + : codegenNode.isComponent + ? CREATE_COMPONENT_BLOCK + : CREATE_ELEMENT_BLOCK + ) } } root.codegenNode = codegenNode @@ -380,7 +400,11 @@ function createRootCodegen(root: RootNode, context: TransformContext) { patchFlag + (__DEV__ ? ` /* ${patchFlagText} */` : ``), undefined, undefined, - true + true, + undefined, + false /* isComponent */, + ShapeFlags.ARRAY_CHILDREN + + (__DEV__ ? ` /* ${ShapeFlagNames[ShapeFlags.ARRAY_CHILDREN]} */` : ``) ) } else { // no children = noop. codegen will return null. diff --git a/packages/compiler-core/src/transforms/hoistStatic.ts b/packages/compiler-core/src/transforms/hoistStatic.ts index 119a1a3856a..4e5455e9fb9 100644 --- a/packages/compiler-core/src/transforms/hoistStatic.ts +++ b/packages/compiler-core/src/transforms/hoistStatic.ts @@ -9,12 +9,28 @@ import { ComponentNode, TemplateNode, VNodeCall, - ParentNode + ParentNode, + JSChildNode, + CallExpression } from '../ast' import { TransformContext } from '../transform' import { PatchFlags, isString, isSymbol } from '@vue/shared' import { isSlotOutlet } from '../utils' -import { CREATE_BLOCK, CREATE_VNODE, OPEN_BLOCK } from '../runtimeHelpers' +import { + OPEN_BLOCK, + CREATE_BLOCK, + CREATE_COMPONENT_VNODE, + CREATE_ELEMENT_VNODE, + CREATE_VNODE, + GUARD_REACTIVE_PROPS, + NORMALIZE_CLASS, + NORMALIZE_PROPS, + NORMALIZE_PROPS_FOR_CLASS, + NORMALIZE_PROPS_FOR_STYLE, + NORMALIZE_STYLE, + CREATE_COMPONENT_BLOCK, + CREATE_ELEMENT_BLOCK +} from '../runtimeHelpers' export function hoistStatic(root: RootNode, context: TransformContext) { walk( @@ -213,9 +229,21 @@ export function getConstantType( // nested updates. if (codegenNode.isBlock) { context.removeHelper(OPEN_BLOCK) - context.removeHelper(CREATE_BLOCK) + context.removeHelper( + context.ssr + ? CREATE_BLOCK + : codegenNode.isComponent + ? CREATE_COMPONENT_BLOCK + : CREATE_ELEMENT_BLOCK + ) codegenNode.isBlock = false - context.helper(CREATE_VNODE) + context.helper( + context.ssr + ? CREATE_VNODE + : codegenNode.isComponent + ? CREATE_COMPONENT_VNODE + : CREATE_ELEMENT_VNODE + ) } constantCache.set(node, returnType) @@ -260,6 +288,34 @@ export function getConstantType( } } +const allowHoistedHelperSet = new Set([ + NORMALIZE_CLASS, + NORMALIZE_STYLE, + NORMALIZE_PROPS, + NORMALIZE_PROPS_FOR_CLASS, + NORMALIZE_PROPS_FOR_STYLE, + GUARD_REACTIVE_PROPS +]) + +function getConstantTypeOfHelperCall( + value: CallExpression, + context: TransformContext +): ConstantTypes { + if ( + value.type === NodeTypes.JS_CALL_EXPRESSION && + allowHoistedHelperSet.has(value.callee) + ) { + const arg = value.arguments[0] as JSChildNode + if (arg.type === NodeTypes.SIMPLE_EXPRESSION) { + return getConstantType(arg, context) + } else if (arg.type === NodeTypes.JS_CALL_EXPRESSION) { + // in the case of nested calls, e.g. `normalizeProps(guardReactiveProps(exp))` + return getConstantTypeOfHelperCall(arg, context) + } + } + return ConstantTypes.NOT_CONSTANT +} + function getGeneratedPropsConstantType( node: PlainElementNode, context: TransformContext @@ -278,6 +334,12 @@ function getGeneratedPropsConstantType( returnType = keyType } if (value.type !== NodeTypes.SIMPLE_EXPRESSION) { + // some function calls can be hoisted, + // such as the `normalizeClass` call generated by the compiler for pre-normalize class, + // in this case we need to respect the ConstanType of the function's argments + if (value.type === NodeTypes.JS_CALL_EXPRESSION) { + return getConstantTypeOfHelperCall(value, context) + } return ConstantTypes.NOT_CONSTANT } const valueType = getConstantType(value, context) diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index dba1a99b487..6e3a4d9356a 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -29,7 +29,9 @@ import { isObject, isReservedProp, capitalize, - camelize + camelize, + ShapeFlags, + ShapeFlagNames } from '@vue/shared' import { createCompilerError, ErrorCodes } from '../errors' import { @@ -37,11 +39,17 @@ import { RESOLVE_COMPONENT, RESOLVE_DYNAMIC_COMPONENT, MERGE_PROPS, + NORMALIZE_CLASS, + NORMALIZE_STYLE, + NORMALIZE_PROPS, + NORMALIZE_PROPS_FOR_CLASS, + NORMALIZE_PROPS_FOR_STYLE, TO_HANDLERS, TELEPORT, KEEP_ALIVE, SUSPENSE, - UNREF + UNREF, + GUARD_REACTIVE_PROPS } from '../runtimeHelpers' import { getInnerRange, @@ -101,6 +109,14 @@ export const transformElement: NodeTransform = (node, context) => { let vnodeDynamicProps: VNodeCall['dynamicProps'] let dynamicPropNames: string[] | undefined let vnodeDirectives: VNodeCall['directives'] + let vnodeShapeFlag: VNodeCall['shapeFlag'] + let shapeFlag: number = !isComponent + ? ShapeFlags.ELEMENT + : vnodeTag === TELEPORT + ? ShapeFlags.TELEPORT + : vnodeTag === SUSPENSE + ? ShapeFlags.SUSPENSE + : 0 let shouldUseBlock = // dynamic component may resolve to plain elements @@ -185,11 +201,14 @@ export const transformElement: NodeTransform = (node, context) => { // (plain / interpolation / expression) if (hasDynamicTextChild || type === NodeTypes.TEXT) { vnodeChildren = child as TemplateTextChildNode + shapeFlag |= ShapeFlags.TEXT_CHILDREN } else { vnodeChildren = node.children + shapeFlag |= ShapeFlags.ARRAY_CHILDREN } } else { vnodeChildren = node.children + shapeFlag |= ShapeFlags.ARRAY_CHILDREN } } @@ -216,6 +235,19 @@ export const transformElement: NodeTransform = (node, context) => { } } + if (shapeFlag > 1) { + if (__DEV__) { + // bitwise flags + const flagNames = Object.keys(ShapeFlagNames) + .map(Number) + .filter(n => shapeFlag & n) + .map(n => ShapeFlagNames[n]) + .join(`, `) + vnodeShapeFlag = shapeFlag + ` /* ${flagNames} */` + } else { + vnodeShapeFlag = String(shapeFlag) + } + } node.codegenNode = createVNodeCall( context, vnodeTag, @@ -226,6 +258,8 @@ export const transformElement: NodeTransform = (node, context) => { vnodeDirectives, !!shouldUseBlock, false /* disableTracking */, + isComponent, + vnodeShapeFlag, node.loc ) } @@ -401,15 +435,24 @@ export function buildProps( // skip if the prop is a cached handler or has constant value return } + if (name === 'ref') { hasRef = true - } else if (name === 'class' && !isComponent) { + } else if (name === 'class') { hasClassBinding = true - } else if (name === 'style' && !isComponent) { + } else if (name === 'style') { hasStyleBinding = true } else if (name !== 'key' && !dynamicPropNames.includes(name)) { dynamicPropNames.push(name) } + + if ( + isComponent && + (name === 'class' || name === 'style') && + !dynamicPropNames.includes(name) + ) { + dynamicPropNames.push(name) + } } else { hasDynamicKeys = true } @@ -627,10 +670,10 @@ export function buildProps( if (hasDynamicKeys) { patchFlag |= PatchFlags.FULL_PROPS } else { - if (hasClassBinding) { + if (hasClassBinding && !isComponent) { patchFlag |= PatchFlags.CLASS } - if (hasStyleBinding) { + if (hasStyleBinding && !isComponent) { patchFlag |= PatchFlags.STYLE } if (dynamicPropNames.length) { @@ -647,6 +690,94 @@ export function buildProps( patchFlag |= PatchFlags.NEED_PATCH } + // pre-normalize props, SSR skip for now + if (!context.forSSR && propsExpression) { + switch (propsExpression.type) { + case NodeTypes.JS_OBJECT_EXPRESSION: + // indicates that there is no v-bind="obj" binding + const helperToUse = doNormalize( + context, + propsExpression.properties, + hasClassBinding, + hasStyleBinding + ) + + if (helperToUse) { + propsExpression = createCallExpression(context.helper(helperToUse), [ + propsExpression + ]) + } + break + case NodeTypes.JS_CALL_EXPRESSION: + // call of `mergeProps` + if (propsExpression.callee === MERGE_PROPS) { + let vBindIndex = -1 + let lastObjectExpressionIndex = -1 + const args = propsExpression.arguments as PropsExpression[] + // 1. find the index of v-bind and object express + for (let i = 0; i < propsExpression.arguments.length; i++) { + const arg = args[i] + if ( + arg.type === NodeTypes.SIMPLE_EXPRESSION || + arg.type === NodeTypes.COMPOUND_EXPRESSION + ) { + // v-bind="obj" --> SIMPLE_EXPRESSION + // v-bind="fn()" --> COMPOUND_EXPRESSION + vBindIndex = i + } else if (arg.type === NodeTypes.JS_OBJECT_EXPRESSION) { + lastObjectExpressionIndex = i + } + } + /** + * 1.

+ * mergeProps(ExpressionNode, ObjectExpression) + * - vBindIndex: 0 + * - objectExpressionIndex: 1 + * 2.

+ * mergeProps(ObjectExpression, ExpressionNode) + * - vBindIndex: 1 + * - objectExpressionIndex: 0 + * 3.

+ * mergeProps(ObjectExpression, ExpressionNode, ObjectExpression) + * - vBindIndex: 1 + * - objectExpressionIndex: 2 + */ + if (vBindIndex < lastObjectExpressionIndex) { + const helperToUse = doNormalize( + context, + (args[lastObjectExpressionIndex] as ObjectExpression).properties, + hasClassBinding, + hasStyleBinding, + false + ) + if (helperToUse) { + propsExpression = createCallExpression( + context.helper(helperToUse), + [propsExpression] + ) + } + } else { + propsExpression = createCallExpression( + context.helper(NORMALIZE_PROPS), + [propsExpression] + ) + } + } + break + default: + // single v-bind + propsExpression = createCallExpression( + context.helper(NORMALIZE_PROPS), + [ + createCallExpression(context.helper(GUARD_REACTIVE_PROPS), [ + propsExpression + ]) + ] + ) + break + } + } + return { props: propsExpression, directives: runtimeDirectives, @@ -655,6 +786,98 @@ export function buildProps( } } +// normalization is only needed when the dynamic binding key is before the binding of class and style +// i.e.

+function doNormalize( + context: TransformContext, + properties: Property[], + hasClassBinding: boolean, + hasStyleBinding: boolean, + treatAbsentAsNormalized: boolean = true +) { + let classKeyIndex = -1 + let styleKeyIndex = -1 + let dynamicKeyIndex = -1 + + for (let i = 0; i < properties.length; i++) { + const p = properties[i] + if (!isStaticExp(p.key)) dynamicKeyIndex = i + if (isStaticExp(p.key) && p.key.content === 'class') classKeyIndex = i + if (isStaticExp(p.key) && p.key.content === 'style') styleKeyIndex = i + } + + /** + * 1. in the case of no dynamic binding key, e.g.

+ * shouldNormalizeClass = true + * shouldNormalizeStyle = true + * 1. in the case of

+ * shouldNormalizeClass = true + * shouldNormalizeStyle = true + * 2. in the case of

+ * shouldNormalizeClass = false + * shouldNormalizeStyle = true + * 3. in the case of

+ * shouldNormalizeClass = false + * shouldNormalizeStyle = false + * 4. in the case of

+ * shouldNormalizeClass = false + * shouldNormalizeStyle = false + */ + const shouldNormalizeClass = dynamicKeyIndex < classKeyIndex + const shouldNormalizeStyle = dynamicKeyIndex < styleKeyIndex + + if (shouldNormalizeClass || shouldNormalizeStyle) { + for (let i = 0; i < properties.length; i++) { + const p = properties[i] + if ( + p.key.type !== NodeTypes.SIMPLE_EXPRESSION || + (p.key.content !== 'class' && p.key.content !== 'style') + ) + continue + + // wrap with normalizeClass/normalizeStyle + let helperToUse = null + if ( + p.key.content === 'class' && + (hasClassBinding || + // class may bind a constant and be skipped during patchFlag analysis, + // but it should still be normalized + // e.g.

+ !isStaticExp(p.value)) + ) { + helperToUse = NORMALIZE_CLASS + } else if ( + p.key.content === 'style' && + (hasClassBinding || !isStaticExp(p.value)) + ) { + helperToUse = NORMALIZE_STYLE + } + + p.value = helperToUse + ? createCallExpression(context.helper(helperToUse), [p.value]) + : p.value + } + } + + const isClassNormalized = + shouldNormalizeClass || (treatAbsentAsNormalized && classKeyIndex === -1) + const isStyleNormalized = + shouldNormalizeStyle || (treatAbsentAsNormalized && styleKeyIndex === -1) + let helperToUse = null + if (!isClassNormalized && !isStyleNormalized) { + // normalize both + helperToUse = NORMALIZE_PROPS + } else if (isClassNormalized && !isStyleNormalized) { + // class has been normalized, only need to normalize style + helperToUse = NORMALIZE_PROPS_FOR_STYLE + } else if (isStyleNormalized && !isClassNormalized) { + // style has been normalized, only need to normalize class + helperToUse = NORMALIZE_PROPS_FOR_CLASS + } + + return helperToUse +} + // Dedupe props in an object literal. // Literal duplicated attributes would have been warned during the parse phase, // however, it's possible to encounter duplicated `onXXX` handlers with different diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index 4060912c648..33d9828f0e9 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -37,13 +37,22 @@ import { import { RENDER_LIST, OPEN_BLOCK, - CREATE_BLOCK, FRAGMENT, + CREATE_COMPONENT_BLOCK, + CREATE_ELEMENT_BLOCK, + CREATE_COMPONENT_VNODE, + CREATE_ELEMENT_VNODE, + CREATE_BLOCK, CREATE_VNODE } from '../runtimeHelpers' import { processExpression } from './transformExpression' import { validateBrowserExpression } from '../validateExpression' -import { PatchFlags, PatchFlagNames } from '@vue/shared' +import { + PatchFlags, + PatchFlagNames, + ShapeFlags, + ShapeFlagNames +} from '@vue/shared' export const transformFor = createStructuralDirectiveTransform( 'for', @@ -85,6 +94,7 @@ export const transformFor = createStructuralDirectiveTransform( : keyProp ? PatchFlags.KEYED_FRAGMENT : PatchFlags.UNKEYED_FRAGMENT + forNode.codegenNode = createVNodeCall( context, helper(FRAGMENT), @@ -96,6 +106,11 @@ export const transformFor = createStructuralDirectiveTransform( undefined, true /* isBlock */, !isStableFragment /* disableTracking */, + false /* isComponent */, + ShapeFlags.ARRAY_CHILDREN + + (__DEV__ + ? ` /* ${ShapeFlagNames[ShapeFlags.ARRAY_CHILDREN]} */` + : ``), node.loc ) as ForCodegenNode @@ -156,7 +171,13 @@ export const transformFor = createStructuralDirectiveTransform( : ``), undefined, undefined, - true + true, + undefined, + false /* isComponent */, + ShapeFlags.ARRAY_CHILDREN + + (__DEV__ + ? ` /* ${ShapeFlagNames[ShapeFlags.ARRAY_CHILDREN]} */` + : ``) ) } else { // Normal element v-for. Directly use the child's codegenNode @@ -179,9 +200,21 @@ export const transformFor = createStructuralDirectiveTransform( childBlock.isBlock = !isStableFragment if (childBlock.isBlock) { helper(OPEN_BLOCK) - helper(CREATE_BLOCK) + helper( + context.forSSR + ? CREATE_BLOCK + : childBlock.isComponent + ? CREATE_COMPONENT_BLOCK + : CREATE_ELEMENT_BLOCK + ) } else { - helper(CREATE_VNODE) + helper( + context.forSSR + ? CREATE_VNODE + : childBlock.isComponent + ? CREATE_COMPONENT_VNODE + : CREATE_ELEMENT_VNODE + ) } } diff --git a/packages/compiler-core/src/transforms/vIf.ts b/packages/compiler-core/src/transforms/vIf.ts index e9089fed6a6..bb9d1b67eca 100644 --- a/packages/compiler-core/src/transforms/vIf.ts +++ b/packages/compiler-core/src/transforms/vIf.ts @@ -28,14 +28,23 @@ import { createCompilerError, ErrorCodes } from '../errors' import { processExpression } from './transformExpression' import { validateBrowserExpression } from '../validateExpression' import { - CREATE_BLOCK, FRAGMENT, CREATE_COMMENT, OPEN_BLOCK, - CREATE_VNODE + CREATE_VNODE, + CREATE_COMPONENT_BLOCK, + CREATE_ELEMENT_BLOCK, + CREATE_BLOCK, + CREATE_COMPONENT_VNODE, + CREATE_ELEMENT_VNODE } from '../runtimeHelpers' import { injectProp, findDir, findProp, isBuiltInType } from '../utils' -import { PatchFlags, PatchFlagNames } from '@vue/shared' +import { + PatchFlags, + PatchFlagNames, + ShapeFlags, + ShapeFlagNames +} from '@vue/shared' export const transformIf = createStructuralDirectiveTransform( /^(if|else|else-if)$/, @@ -278,6 +287,11 @@ function createChildrenCodegenNode( undefined, true, false, + false /* isComponent */, + ShapeFlags.ARRAY_CHILDREN + + (__DEV__ + ? ` /* ${ShapeFlagNames[ShapeFlags.ARRAY_CHILDREN]} */` + : ``), branch.loc ) } @@ -286,10 +300,22 @@ function createChildrenCodegenNode( .codegenNode as BlockCodegenNode // Change createVNode to createBlock. if (vnodeCall.type === NodeTypes.VNODE_CALL && !vnodeCall.isBlock) { - removeHelper(CREATE_VNODE) + removeHelper( + context.forSSR + ? CREATE_VNODE + : vnodeCall.isComponent + ? CREATE_COMPONENT_VNODE + : CREATE_ELEMENT_VNODE + ) vnodeCall.isBlock = true helper(OPEN_BLOCK) - helper(CREATE_BLOCK) + helper( + context.forSSR + ? CREATE_BLOCK + : vnodeCall.isComponent + ? CREATE_COMPONENT_BLOCK + : CREATE_ELEMENT_BLOCK + ) } // inject branch key injectProp(vnodeCall, keyProperty, context) diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts index d69446d8909..d8f18e7af64 100644 --- a/packages/compiler-core/src/utils.ts +++ b/packages/compiler-core/src/utils.ts @@ -30,9 +30,14 @@ import { SUSPENSE, KEEP_ALIVE, BASE_TRANSITION, - TO_HANDLERS + TO_HANDLERS, + NORMALIZE_PROPS, + NORMALIZE_PROPS_FOR_CLASS, + NORMALIZE_PROPS_FOR_STYLE, + GUARD_REACTIVE_PROPS } from './runtimeHelpers' import { isString, isObject, hyphenate, extend } from '@vue/shared' +import { PropsExpression } from './transforms/transformElement' export const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode => p.type === NodeTypes.SIMPLE_EXPRESSION && p.isStatic @@ -215,14 +220,73 @@ export function isSlotOutlet( return node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.SLOT } +const propsHelperSet = new Set([ + NORMALIZE_PROPS, + NORMALIZE_PROPS_FOR_CLASS, + NORMALIZE_PROPS_FOR_STYLE, + GUARD_REACTIVE_PROPS +]) + +function getUnnormalizedProps( + props: PropsExpression | '{}', + callPath: CallExpression[] = [] +): [PropsExpression | '{}', CallExpression[]] { + if ( + props && + !isString(props) && + props.type === NodeTypes.JS_CALL_EXPRESSION + ) { + const callee = props.callee + if (!isString(callee) && propsHelperSet.has(callee)) { + return getUnnormalizedProps( + props.arguments[0] as PropsExpression, + callPath.concat(props) + ) + } + } + return [props, callPath] +} export function injectProp( node: VNodeCall | RenderSlotCall, prop: Property, context: TransformContext ) { let propsWithInjection: ObjectExpression | CallExpression | undefined - const props = + const originalProps = node.type === NodeTypes.VNODE_CALL ? node.props : node.arguments[2] + + /** + * 1. mergeProps(...) + * 2. toHandlers(...) + * + * 3. normalizeProps(...) + * 4. normalizeProps(mergeProps(...)) + * + * 5. normalizePropsForClass(...) + * 6. normalizePropsForClass(mergeProps(...)) + * + * 7. normalizePropsForStyle(...) + * 8. normalizePropsForStyle(mergeProps(...)) + * + * 9. guardReactiveProps(...) + * 10. normalizeProps(guardReactiveProps(...)) + * + * we need to get the real props before normalization + */ + let props = originalProps + let callPath: CallExpression[] = [] + let parentCall: CallExpression | undefined + if ( + props && + !isString(props) && + props.type === NodeTypes.JS_CALL_EXPRESSION + ) { + const ret = getUnnormalizedProps(props) + props = ret[0] + callPath = ret[1] + parentCall = callPath[callPath.length - 1] + } + if (props == null || isString(props)) { propsWithInjection = createObjectExpression([prop]) } else if (props.type === NodeTypes.JS_CALL_EXPRESSION) { @@ -265,11 +329,25 @@ export function injectProp( createObjectExpression([prop]), props ]) + // in the case of `normalizeProps(guardReactiveProps(props))`, + // will be rewritten as `normalizeProps(mergeProps({ key: 0 }, props))`, + // `guardReactiveProps` will no longer be needed + if (parentCall && parentCall.callee === GUARD_REACTIVE_PROPS) { + parentCall = callPath[callPath.length - 2] + } } if (node.type === NodeTypes.VNODE_CALL) { - node.props = propsWithInjection + if (parentCall) { + parentCall.arguments[0] = propsWithInjection + } else { + node.props = propsWithInjection + } } else { - node.arguments[2] = propsWithInjection + if (parentCall) { + parentCall.arguments[0] = propsWithInjection + } else { + node.arguments[2] = propsWithInjection + } } } diff --git a/packages/compiler-sfc/__tests__/compileScript.spec.ts b/packages/compiler-sfc/__tests__/compileScript.spec.ts index d74d7448aa9..12bfaa5db50 100644 --- a/packages/compiler-sfc/__tests__/compileScript.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript.spec.ts @@ -218,9 +218,9 @@ const myEmit = defineEmit(['foo', 'bar']) { inlineTemplate: true } ) expect(content).toMatch('[_unref(myDir)]') - expect(content).toMatch('_createVNode(ChildComp)') + expect(content).toMatch('_createComponentVNode(ChildComp)') // kebab-case component support - expect(content).toMatch('_createVNode(SomeOtherComp)') + expect(content).toMatch('_createComponentVNode(SomeOtherComp)') assertCode(content) }) @@ -245,7 +245,7 @@ const myEmit = defineEmit(['foo', 'bar']) { inlineTemplate: true } ) // no need to unref vue component import - expect(content).toMatch(`createVNode(Foo,`) + expect(content).toMatch(`createComponentVNode(Foo,`) // #2699 should unref named imports from .vue expect(content).toMatch(`unref(bar)`) // should unref other imports diff --git a/packages/compiler-ssr/src/index.ts b/packages/compiler-ssr/src/index.ts index 6b981f18899..29a98e36dac 100644 --- a/packages/compiler-ssr/src/index.ts +++ b/packages/compiler-ssr/src/index.ts @@ -35,6 +35,7 @@ export function compile( // apply DOM-specific parsing options ...parserOptions, ssr: true, + forSSR: true, scopeId: options.mode === 'function' ? null : options.scopeId, // always prefix since compiler-ssr doesn't have size concern prefixIdentifiers: true, diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts index bcfc7a1e687..471bf655183 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts @@ -240,6 +240,7 @@ function createVNodeSlotBranch( ): ReturnStatement { // apply a sub-transform using vnode-based transforms. const rawOptions = rawOptionsMap.get(parentContext.root)! + const subOptions = { ...rawOptions, // overwrite with vnode-based transforms diff --git a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts index bddd7d1d318..a5dbf00a739 100644 --- a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts +++ b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts @@ -20,7 +20,13 @@ import { onBeforeUnmount, createTextVNode, SetupContext, - createApp + createApp, + createElementVNode, + reactive, + guardReactiveProps, + normalizeProps, + normalizeClass, + normalizeStyle } from '@vue/runtime-test' import { PatchFlags, SlotFlags } from '@vue/shared' import { SuspenseImpl } from '../src/components/Suspense' @@ -821,4 +827,39 @@ describe('renderer: optimized mode', () => { await nextTick() expect(inner(root)).toBe('

true
') }) + describe('optimized VNode creation', () => { + test('single v-bind', () => { + // in case
+ // 1. use `guardReactiveProps` to make sure it gets rid of reactive data + // 2. use `normalizeProps` to normalize `class` and `style` + const p = createElementVNode( + 'p', + normalizeProps( + guardReactiveProps(reactive({ class: ['foo', { bar: true }] })) + ) + ) + render(p, root) + expect(inner(root)).toBe('

') + }) + + test('dynamic class binding', () => { + // in case
+ const p = createElementVNode('p', { + class: normalizeClass([{ foo: true }, 'bar']) + }) + render(p, root) + expect(inner(root)).toBe('

') + }) + + test('dynamic style binding', () => { + // in case
+ const p = createElementVNode('p', { + style: normalizeStyle([['background: green;'], { color: 'red' }]) + }) + render(p, root) + expect(inner(root)).toBe( + '

' + ) + }) + }) }) diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index bb90574d071..762f80f73c2 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -245,13 +245,23 @@ export { setBlockTracking, createTextVNode, createCommentVNode, - createStaticVNode + createStaticVNode, + createElementVNode, + createComponentVNode, + createElementBlock, + createComponentBlock, + guardReactiveProps } from './vnode' export { toDisplayString, camelize, capitalize, - toHandlerKey + toHandlerKey, + normalizeProps, + normalizeClass, + normalizeStyle, + normalizePropsForStyle, + normalizePropsForClass } from '@vue/shared' // For test-utils diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index ec5a8f5dc61..8c2e71eaa2e 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -243,6 +243,90 @@ export function setBlockTracking(value: number) { isBlockTreeEnabled += value } +function setupBlock(vnode: VNode) { + // save current block children on the block vnode + vnode.dynamicChildren = isBlockTreeEnabled > 0 ? currentBlock || (EMPTY_ARR as any) : null + // close block + closeBlock() + // a block is always going to be patched, so track it as a child of its + // parent block + if (isBlockTreeEnabled > 0 && currentBlock) { + currentBlock.push(vnode) + } +} + +function trackVNode( + vnode: VNode, + isBlockNode: boolean, + patchFlag: number, + shapeFlag: number +) { + if ( + isBlockTreeEnabled > 0 && + // avoid a block node from tracking itself + !isBlockNode && + // has current parent block + currentBlock && + // presence of a patch flag indicates this node needs patching on updates. + // component nodes also should always be patched, because even if the + // component doesn't need to update, it needs to persist the instance on to + // the next vnode so that it can be properly unmounted later. + (patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) && + // the EVENTS flag is only for hydration and if it is the only flag, the + // vnode should not be considered dynamic due to handler caching. + patchFlag !== PatchFlags.HYDRATE_EVENTS + ) { + currentBlock.push(vnode) + } +} + +export function createElementBlock( + type: string, + props?: Record | null, + children?: any, + patchFlag?: number, + dynamicProps?: string[], + shapeFlag?: number +) { + const vnode = createElementVNode( + type, + props, + children, + patchFlag, + dynamicProps, + shapeFlag, + true /* isBlock */ + ) + setupBlock(vnode) + return vnode +} + +export function createComponentBlock( + type: + | Component + | VNode + | typeof NULL_DYNAMIC_COMPONENT + | typeof TeleportImpl + | typeof SuspenseImpl, + props?: Record | null, + children?: any, + patchFlag?: number, + dynamicProps?: string[], + shapeFlag?: number +) { + const vnode = createComponentVNode( + type, + props, + children, + patchFlag, + dynamicProps, + shapeFlag, + true /* isBlock */ + ) + setupBlock(vnode) + return vnode +} + /** * Create a block root vnode. Takes the same exact arguments as `createVNode`. * A block root keeps track of dynamic nodes within the block in the @@ -265,16 +349,7 @@ export function createBlock( dynamicProps, true /* isBlock: prevent a block from tracking itself */ ) - // save current block children on the block vnode - vnode.dynamicChildren = - isBlockTreeEnabled > 0 ? currentBlock || (EMPTY_ARR as any) : null - // close block - closeBlock() - // a block is always going to be patched, so track it as a child of its - // parent block - if (isBlockTreeEnabled > 0 && currentBlock) { - currentBlock.push(vnode) - } + setupBlock(vnode) return vnode } @@ -338,6 +413,143 @@ export const createVNode = (__DEV__ ? createVNodeWithArgsTransform : _createVNode) as typeof _createVNode +export function createElementVNode( + type: string, + props: (Data & VNodeProps) | null = null, + children: Exclude = null, + patchFlag: number = 0, + dynamicProps: string[] | null = null, + shapeFlag = ShapeFlags.ELEMENT, + isBlockNode = false +) { + const vnode: VNode = { + __v_isVNode: true, + [ReactiveFlags.SKIP]: true, + type, + props, + key: props && normalizeKey(props), + ref: props && normalizeRef(props), + scopeId: currentScopeId, + slotScopeIds: null, + children, + component: null, + suspense: null, + ssContent: null, + ssFallback: null, + dirs: null, + transition: null, + el: null, + anchor: null, + target: null, + targetAnchor: null, + staticCount: 0, + shapeFlag, + patchFlag, + dynamicProps, + dynamicChildren: null, + appContext: null + } + + trackVNode(vnode, isBlockNode, patchFlag, shapeFlag) + + return vnode +} + +export function createComponentVNode( + type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT, + props: (Data & VNodeProps) | null = null, + children: Exclude = null, + patchFlag: number = 0, + dynamicProps: string[] | null = null, + shapeFlag: ShapeFlags = 0, + isBlockNode = false +) { + if (!type || type === NULL_DYNAMIC_COMPONENT) { + if (__DEV__ && !type) { + warn(`Invalid vnode type when creating vnode: ${type}.`) + } + type = Comment + } + + if (isVNode(type)) { + const cloned = cloneVNode(type, props, true /* mergeRef: true */) + if (children) { + normalizeChildren(cloned, children) + } + return cloned + } + + // class component normalization. + if (isClassComponent(type)) { + type = type.__vccOpts + } + + // Suspense and Teleport analysis at compile time + if (!shapeFlag) { + shapeFlag = isObject(type) + ? ShapeFlags.STATEFUL_COMPONENT + : isFunction(type) + ? ShapeFlags.FUNCTIONAL_COMPONENT + : 0 + } + + if (__DEV__ && shapeFlag & ShapeFlags.STATEFUL_COMPONENT && isProxy(type)) { + type = toRaw(type) + warn( + `Vue received a Component which was made a reactive object. This can ` + + `lead to unnecessary performance overhead, and should be avoided by ` + + `marking the component with \`markRaw\` or using \`shallowRef\` ` + + `instead of \`ref\`.`, + `\nComponent that was made reactive: `, + type + ) + } + + const vnode: VNode = { + __v_isVNode: true, + [ReactiveFlags.SKIP]: true, + type, + props, + key: props && normalizeKey(props), + ref: props && normalizeRef(props), + scopeId: currentScopeId, + slotScopeIds: null, + children: null, + component: null, + suspense: null, + ssContent: null, + ssFallback: null, + dirs: null, + transition: null, + el: null, + anchor: null, + target: null, + targetAnchor: null, + staticCount: 0, + shapeFlag, + patchFlag, + dynamicProps, + dynamicChildren: null, + appContext: null + } + + // validate key + if (__DEV__ && vnode.key !== vnode.key) { + warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type) + } + + normalizeChildren(vnode, children) + + // normalize suspense children + if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) { + ;(type as typeof SuspenseImpl).normalize(vnode) + } + + trackVNode(vnode, isBlockNode, patchFlag, shapeFlag) + + return vnode +} + function _createVNode( type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT, props: (Data & VNodeProps) | null = null, @@ -486,6 +698,13 @@ function _createVNode( return vnode } +export function guardReactiveProps(props: (Data & VNodeProps) | null) { + if (!props) return null + return isProxy(props) || InternalObjectKey in props + ? extend({}, props) + : props +} + export function cloneVNode( vnode: VNode, extraProps?: Data & VNodeProps | null, diff --git a/packages/shared/src/normalizeProp.ts b/packages/shared/src/normalizeProp.ts index 76eec4b582d..6fe8489df15 100644 --- a/packages/shared/src/normalizeProp.ts +++ b/packages/shared/src/normalizeProp.ts @@ -76,3 +76,32 @@ export function normalizeClass(value: unknown): string { } return res.trim() } + +export function normalizeProps(props: Record | null) { + if (!props) return null + let { class: klass, style } = props + if (klass && !isString(klass)) { + props.class = normalizeClass(klass) + } + if (style) { + props.style = normalizeStyle(style) + } + return props +} + +export function normalizePropsForStyle(props: Record | null) { + if (!props) return null + if (props.style) { + props.style = normalizeStyle(props.style) + } + return props +} + +export function normalizePropsForClass(props: Record | null) { + if (!props) return null + const { class: klass } = props + if (klass && !isString(klass)) { + props.class = normalizeClass(klass) + } + return props +} diff --git a/packages/shared/src/shapeFlags.ts b/packages/shared/src/shapeFlags.ts index 8defb8a3bf3..366b48b6d19 100644 --- a/packages/shared/src/shapeFlags.ts +++ b/packages/shared/src/shapeFlags.ts @@ -11,3 +11,20 @@ export const enum ShapeFlags { COMPONENT_KEPT_ALIVE = 1 << 9, COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT } + +/** + * dev only ShapeFlag -> name mapping + */ +export const ShapeFlagNames = { + [ShapeFlags.ELEMENT]: `ELEMENT`, + [ShapeFlags.FUNCTIONAL_COMPONENT]: `FUNCTIONAL_COMPONENT`, + [ShapeFlags.STATEFUL_COMPONENT]: `STATEFUL_COMPONENT`, + [ShapeFlags.TEXT_CHILDREN]: `TEXT_CHILDREN`, + [ShapeFlags.ARRAY_CHILDREN]: `ARRAY_CHILDREN`, + [ShapeFlags.SLOTS_CHILDREN]: `SLOTS_CHILDREN`, + [ShapeFlags.TELEPORT]: `TELEPORT`, + [ShapeFlags.SUSPENSE]: `SUSPENSE`, + [ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE]: `COMPONENT_SHOULD_KEEP_ALIVE`, + [ShapeFlags.COMPONENT_KEPT_ALIVE]: `COMPONENT_KEPT_ALIVE`, + [ShapeFlags.COMPONENT]: `COMPONENT` +} From 801c72e65dd65785f396bc0115fda821709f3219 Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Mon, 1 Mar 2021 22:04:27 +0800 Subject: [PATCH 02/15] test: update snapshots --- .../__snapshots__/codegen.spec.ts.snap | 10 +- .../__snapshots__/compile.spec.ts.snap | 72 ++--- .../__snapshots__/scopeId.spec.ts.snap | 36 +-- .../compiler-core/__tests__/codegen.spec.ts | 6 +- .../__snapshots__/hoistStatic.spec.ts.snap | 260 +++++++++--------- .../transformExpressions.spec.ts.snap | 6 +- .../__snapshots__/transformText.spec.ts.snap | 42 +-- .../__snapshots__/vFor.spec.ts.snap | 124 ++++----- .../transforms/__snapshots__/vIf.spec.ts.snap | 60 ++-- .../__snapshots__/vModel.spec.ts.snap | 32 +-- .../__snapshots__/vOnce.spec.ts.snap | 34 +-- .../__snapshots__/vSlot.spec.ts.snap | 54 ++-- .../src/transforms/transformElement.ts | 4 +- .../__snapshots__/index.spec.ts.snap | 20 +- .../__snapshots__/vModel.spec.ts.snap | 54 ++-- .../__snapshots__/vShow.spec.ts.snap | 4 +- .../transforms/transformStyle.spec.ts | 15 +- .../__snapshots__/compileScript.spec.ts.snap | 86 +++--- .../compileTemplate.spec.ts.snap | 2 +- .../templateTransformAssetUrl.spec.ts.snap | 68 ++--- .../templateTransformSrcset.spec.ts.snap | 90 +++--- 21 files changed, 547 insertions(+), 532 deletions(-) diff --git a/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap index 74d0ab70d38..f15432c7de9 100644 --- a/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap @@ -48,13 +48,13 @@ exports[`compiler: codegen Element (callExpression + objectExpression + Template " return function render(_ctx, _cache) { with (_ctx) { - return _createVNode(\\"div\\", { + return _createElementVNode(\\"div\\", { id: \\"foo\\", [prop]: bar, [foo + bar]: bar }, [ - _createVNode(\\"p\\", { \\"some-key\\": \\"foo\\" }) - ], 16) + _createElementVNode(\\"p\\", { \\"some-key\\": \\"foo\\" }, null, null, null, 1) + ], 16, null, 1) } }" `; @@ -98,7 +98,7 @@ exports[`compiler: codegen forNode 1`] = ` " return function render(_ctx, _cache) { with (_ctx) { - return (_openBlock(true), _createBlock(_Fragment, null, _renderList(), 1)) + return (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(), 1)) } }" `; @@ -107,7 +107,7 @@ exports[`compiler: codegen forNode with constant expression 1`] = ` " return function render(_ctx, _cache) { with (_ctx) { - return (_openBlock(), _createBlock(_Fragment, null, _renderList(), 64 /* STABLE_FRAGMENT */)) + return (_openBlock(), _createElementBlock(_Fragment, null, _renderList(), 64 /* STABLE_FRAGMENT */)) } }" `; diff --git a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap index 6879301b64d..f77a9a4bce2 100644 --- a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap @@ -5,70 +5,70 @@ exports[`compiler: integration tests function mode 1`] = ` return function render(_ctx, _cache) { with (_ctx) { - const { toDisplayString: _toDisplayString, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, createVNode: _createVNode } = _Vue + const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, normalizeClass: _normalizeClass } = _Vue - return (_openBlock(), _createBlock(\\"div\\", { + return (_openBlock(), _createElementBlock(\\"div\\", { id: \\"foo\\", - class: bar.baz + class: _normalizeClass(bar.baz) }, [ _createTextVNode(_toDisplayString(world.burn()) + \\" \\", 1 /* TEXT */), ok - ? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, \\"yes\\")) - : (_openBlock(), _createBlock(_Fragment, { key: 1 }, [ + ? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\", null, null, 9 /* ELEMENT, TEXT_CHILDREN */)) + : (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [ _createTextVNode(\\"no\\") - ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)), - (_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (value, index) => { - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */) - ])) - }), 256 /* UNKEYED_FRAGMENT */)) - ], 2 /* CLASS */)) + ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)), + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (value, index) => { + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */, null, 9 /* ELEMENT, TEXT_CHILDREN */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) + }), 256 /* UNKEYED_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)) + ], 2 /* CLASS */, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = ` -"const { toDisplayString: _toDisplayString, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, createVNode: _createVNode } = Vue +"const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, normalizeClass: _normalizeClass } = Vue return function render(_ctx, _cache) { - return (_openBlock(), _createBlock(\\"div\\", { + return (_openBlock(), _createElementBlock(\\"div\\", { id: \\"foo\\", - class: _ctx.bar.baz + class: _normalizeClass(_ctx.bar.baz) }, [ _createTextVNode(_toDisplayString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */), (_ctx.ok) - ? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, \\"yes\\")) - : (_openBlock(), _createBlock(_Fragment, { key: 1 }, [ + ? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\", null, null, 9 /* ELEMENT, TEXT_CHILDREN */)) + : (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [ _createTextVNode(\\"no\\") - ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)), - (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => { - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */) - ])) - }), 256 /* UNKEYED_FRAGMENT */)) - ], 2 /* CLASS */)) + ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)), + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => { + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */, null, 9 /* ELEMENT, TEXT_CHILDREN */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) + }), 256 /* UNKEYED_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)) + ], 2 /* CLASS */, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) }" `; exports[`compiler: integration tests module mode 1`] = ` -"import { toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock, createCommentVNode as _createCommentVNode, createTextVNode as _createTextVNode, Fragment as _Fragment, renderList as _renderList, createVNode as _createVNode } from \\"vue\\" +"import { toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock, createCommentVNode as _createCommentVNode, createTextVNode as _createTextVNode, Fragment as _Fragment, renderList as _renderList, normalizeClass as _normalizeClass } from \\"vue\\" export function render(_ctx, _cache) { - return (_openBlock(), _createBlock(\\"div\\", { + return (_openBlock(), _createElementBlock(\\"div\\", { id: \\"foo\\", - class: _ctx.bar.baz + class: _normalizeClass(_ctx.bar.baz) }, [ _createTextVNode(_toDisplayString(_ctx.world.burn()) + \\" \\", 1 /* TEXT */), (_ctx.ok) - ? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, \\"yes\\")) - : (_openBlock(), _createBlock(_Fragment, { key: 1 }, [ + ? (_openBlock(), _createElementBlock(\\"div\\", { key: 0 }, \\"yes\\", null, null, 9 /* ELEMENT, TEXT_CHILDREN */)) + : (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [ _createTextVNode(\\"no\\") - ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)), - (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => { - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */) - ])) - }), 256 /* UNKEYED_FRAGMENT */)) - ], 2 /* CLASS */)) + ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)), + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (value, index) => { + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createElementVNode(\\"span\\", null, _toDisplayString(value + index), 1 /* TEXT */, null, 9 /* ELEMENT, TEXT_CHILDREN */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) + }), 256 /* UNKEYED_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)) + ], 2 /* CLASS */, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) }" `; diff --git a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap index aa74c6c43d1..bea3954a0f2 100644 --- a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap @@ -1,33 +1,33 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`scopeId compiler support should push scopeId for hoisted nodes 1`] = ` -"import { createVNode as _createVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \\"vue\\" +"import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createElementBlock as _createElementBlock, withScopeId as _withScopeId, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \\"vue\\" const _withId = /*#__PURE__*/_withScopeId(\\"test\\") _pushScopeId(\\"test\\") -const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"hello\\", -1 /* HOISTED */) -const _hoisted_2 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */) -_popScopeId() +const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"div\\", null, \\"hello\\", -1 /* HOISTED */, null, 9 /* ELEMENT, TEXT_CHILDREN */) +const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */, null, 9 /* ELEMENT, TEXT_CHILDREN */) +_popScopeId(null) -export const render = /*#__PURE__*/_withId((_ctx, _cache) => { - return (_openBlock(), _createBlock(\\"div\\", null, [ +export function render(_ctx, _cache) => { + return (_openBlock(), _createElementBlock(\\"div\\", null, [ _hoisted_1, _createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */), _hoisted_2 - ])) -})" + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) +}" `; exports[`scopeId compiler support should wrap default slot 1`] = ` -"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" +"import { createElementVNode as _createElementVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, createComponentVNode as _createComponentVNode, openBlock as _openBlock, createComponentBlock as _createComponentBlock, withScopeId as _withScopeId, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \\"vue\\" const _withId = /*#__PURE__*/_withScopeId(\\"test\\") export const render = /*#__PURE__*/_withId((_ctx, _cache) => { const _component_Child = _resolveComponent(\\"Child\\") - return (_openBlock(), _createBlock(_component_Child, null, { + return (_openBlock(), _createComponentBlock(_component_Child, null, { default: _withId(() => [ - _createVNode(\\"div\\") + _createElementVNode(\\"div\\") ]), _: 1 /* STABLE */ })) @@ -35,18 +35,18 @@ export const render = /*#__PURE__*/_withId((_ctx, _cache) => { `; exports[`scopeId compiler support should wrap dynamic slots 1`] = ` -"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" +"import { createElementVNode as _createElementVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, createComponentVNode as _createComponentVNode, openBlock as _openBlock, createComponentBlock as _createComponentBlock, withScopeId as _withScopeId } from \\"vue\\" const _withId = /*#__PURE__*/_withScopeId(\\"test\\") export const render = /*#__PURE__*/_withId((_ctx, _cache) => { const _component_Child = _resolveComponent(\\"Child\\") - return (_openBlock(), _createBlock(_component_Child, null, _createSlots({ _: 2 /* DYNAMIC */ }, [ + return (_openBlock(), _createComponentBlock(_component_Child, null, _createSlots({ _: 2 /* DYNAMIC */ }, [ (_ctx.ok) ? { name: \\"foo\\", fn: _withId(() => [ - _createVNode(\\"div\\") + _createElementVNode(\\"div\\") ]) } : undefined, @@ -54,7 +54,7 @@ export const render = /*#__PURE__*/_withId((_ctx, _cache) => { return { name: i, fn: _withId(() => [ - _createVNode(\\"div\\") + _createElementVNode(\\"div\\") ]) } }) @@ -63,18 +63,18 @@ export const render = /*#__PURE__*/_withId((_ctx, _cache) => { `; exports[`scopeId compiler support should wrap named slots 1`] = ` -"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" +"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, createComponentVNode as _createComponentVNode, openBlock as _openBlock, createComponentBlock as _createComponentBlock, withScopeId as _withScopeId } from \\"vue\\" const _withId = /*#__PURE__*/_withScopeId(\\"test\\") export const render = /*#__PURE__*/_withId((_ctx, _cache) => { const _component_Child = _resolveComponent(\\"Child\\") - return (_openBlock(), _createBlock(_component_Child, null, { + return (_openBlock(), _createComponentBlock(_component_Child, null, { foo: _withId(({ msg }) => [ _createTextVNode(_toDisplayString(msg), 1 /* TEXT */) ]), bar: _withId(() => [ - _createVNode(\\"div\\") + _createElementVNode(\\"div\\") ]), _: 1 /* STABLE */ })) diff --git a/packages/compiler-core/__tests__/codegen.spec.ts b/packages/compiler-core/__tests__/codegen.spec.ts index 61908a9105b..f542caf0637 100644 --- a/packages/compiler-core/__tests__/codegen.spec.ts +++ b/packages/compiler-core/__tests__/codegen.spec.ts @@ -401,8 +401,10 @@ describe('compiler: codegen', () => { [prop]: bar, [foo + bar]: bar }, [ - _${helperNameMap[CREATE_ELEMENT_VNODE]}("p", { "some-key": "foo" }) - ], ${PatchFlags.FULL_PROPS})`) + _${ + helperNameMap[CREATE_ELEMENT_VNODE] + }("p", { "some-key": "foo" }, null, null, null, 1) + ], ${PatchFlags.FULL_PROPS}, null, 1)`) expect(code).toMatchSnapshot() }) diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap index 022b243e367..415c1fce1ca 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap @@ -2,217 +2,219 @@ exports[`compiler: hoistStatic transform hoist element with static key 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", { key: \\"foo\\" }, null, -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"div\\", { key: \\"foo\\" }, null, -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { - const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ _hoisted_1 - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform hoist nested static tree 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/_createVNode(\\"p\\", null, [ - /*#__PURE__*/_createVNode(\\"span\\"), - /*#__PURE__*/_createVNode(\\"span\\") -], -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"p\\", null, [ + /*#__PURE__*/_createElementVNode(\\"span\\"), + /*#__PURE__*/_createElementVNode(\\"span\\") +], -1 /* HOISTED */, null, 17 /* ELEMENT, ARRAY_CHILDREN */) return function render(_ctx, _cache) { with (_ctx) { - const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ _hoisted_1 - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform hoist nested static tree with comments 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _Vue +const { createElementVNode: _createElementVNode, createCommentVNode: _createCommentVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, [ +const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"div\\", null, [ /*#__PURE__*/_createCommentVNode(\\"comment\\") -], -1 /* HOISTED */) +], -1 /* HOISTED */, null, 17 /* ELEMENT, ARRAY_CHILDREN */) return function render(_ctx, _cache) { with (_ctx) { - const { createCommentVNode: _createCommentVNode, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { createCommentVNode: _createCommentVNode, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ _hoisted_1 - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform hoist siblings with common non-hoistable parent 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/_createVNode(\\"span\\", null, null, -1 /* HOISTED */) -const _hoisted_2 = /*#__PURE__*/_createVNode(\\"div\\", null, null, -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"span\\", null, null, -1 /* HOISTED */) +const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"div\\", null, null, -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { - const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ _hoisted_1, _hoisted_2 - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform hoist simple element 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/_createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\", -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\", -1 /* HOISTED */, null, 9 /* ELEMENT, TEXT_CHILDREN */) return function render(_ctx, _cache) { with (_ctx) { - const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ _hoisted_1 - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform hoist static props for elements with directives 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue const _hoisted_1 = { id: \\"foo\\" } return function render(_ctx, _cache) { with (_ctx) { - const { resolveDirective: _resolveDirective, createVNode: _createVNode, withDirectives: _withDirectives, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { resolveDirective: _resolveDirective, createElementVNode: _createElementVNode, withDirectives: _withDirectives, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue const _directive_foo = _resolveDirective(\\"foo\\") - return (_openBlock(), _createBlock(\\"div\\", null, [ - _withDirectives(_createVNode(\\"div\\", _hoisted_1, null, 512 /* NEED_PATCH */), [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _withDirectives(_createElementVNode(\\"div\\", _hoisted_1, null, 512 /* NEED_PATCH */), [ [_directive_foo] ]) - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform hoist static props for elements with dynamic text children 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue const _hoisted_1 = { id: \\"foo\\" } return function render(_ctx, _cache) { with (_ctx) { - const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(\\"div\\", _hoisted_1, _toDisplayString(hello), 1 /* TEXT */) - ])) + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createElementVNode(\\"div\\", _hoisted_1, _toDisplayString(hello), 1 /* TEXT */, null, 9 /* ELEMENT, TEXT_CHILDREN */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform hoist static props for elements with unhoistable children 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode, createComponentVNode: _createComponentVNode } = _Vue const _hoisted_1 = { id: \\"foo\\" } return function render(_ctx, _cache) { with (_ctx) { - const { resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { resolveComponent: _resolveComponent, createComponentVNode: _createComponentVNode, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue const _component_Comp = _resolveComponent(\\"Comp\\") - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(\\"div\\", _hoisted_1, [ - _createVNode(_component_Comp) - ]) - ])) + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createElementVNode(\\"div\\", _hoisted_1, [ + _createComponentVNode(_component_Comp) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform prefixIdentifiers hoist class with static object value 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue -const _hoisted_1 = { class: { foo: true } } +const _hoisted_1 = { + class: /*#__PURE__*/_normalizeClass({ foo: true }) +} return function render(_ctx, _cache) { with (_ctx) { - const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(\\"span\\", _hoisted_1, _toDisplayString(_ctx.bar), 1 /* TEXT */) - ])) + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createElementVNode(\\"span\\", _hoisted_1, _toDisplayString(_ctx.bar), 1 /* TEXT */, null, 9 /* ELEMENT, TEXT_CHILDREN */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static tree with static interpolation 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/_createVNode(\\"span\\", null, \\"foo \\" + /*#__PURE__*/_toDisplayString(1) + \\" \\" + /*#__PURE__*/_toDisplayString(true), -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"span\\", null, \\"foo \\" + /*#__PURE__*/_toDisplayString(1) + \\" \\" + /*#__PURE__*/_toDisplayString(true), -1 /* HOISTED */, null, 9 /* ELEMENT, TEXT_CHILDREN */) return function render(_ctx, _cache) { with (_ctx) { - const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ _hoisted_1 - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static tree with static prop value 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue -const _hoisted_1 = /*#__PURE__*/_createVNode(\\"span\\", { foo: 0 }, /*#__PURE__*/_toDisplayString(1), -1 /* HOISTED */) +const _hoisted_1 = /*#__PURE__*/_createElementVNode(\\"span\\", { foo: 0 }, /*#__PURE__*/_toDisplayString(1), -1 /* HOISTED */, null, 9 /* ELEMENT, TEXT_CHILDREN */) return function render(_ctx, _cache) { with (_ctx) { - const { toDisplayString: _toDisplayString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ _hoisted_1 - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist elements with cached handlers 1`] = ` -"import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\" +"import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \\"vue\\" export function render(_ctx, _cache) { - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(\\"div\\", null, [ - _createVNode(\\"div\\", { + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createElementVNode(\\"div\\", null, [ + _createElementVNode(\\"div\\", { onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.foo && _ctx.foo(...args))) }) - ]) - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) }" `; @@ -221,15 +223,15 @@ exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expr return function render(_ctx, _cache) { with (_ctx) { - const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, toDisplayString: _toDisplayString, createVNode: _createVNode } = _Vue - - return (_openBlock(), _createBlock(\\"div\\", null, [ - (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (o) => { - return (_openBlock(), _createBlock(\\"p\\", null, [ - _createVNode(\\"span\\", null, _toDisplayString(o + 'foo'), 1 /* TEXT */) - ])) - }), 256 /* UNKEYED_FRAGMENT */)) - ])) + const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, toDisplayString: _toDisplayString, createElementVNode: _createElementVNode } = _Vue + + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (o) => { + return (_openBlock(), _createElementBlock(\\"p\\", null, [ + _createElementVNode(\\"span\\", null, _toDisplayString(o + 'foo'), 1 /* TEXT */, null, 9 /* ELEMENT, TEXT_CHILDREN */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) + }), 256 /* UNKEYED_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; @@ -239,11 +241,11 @@ exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expr return function render(_ctx, _cache) { with (_ctx) { - const { toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, createComponentVNode: _createComponentVNode, openBlock: _openBlock, createComponentBlock: _createComponentBlock } = _Vue const _component_Comp = _resolveComponent(\\"Comp\\") - return (_openBlock(), _createBlock(_component_Comp, null, { + return (_openBlock(), _createComponentBlock(_component_Comp, null, { default: _withCtx(({ foo }) => [ _createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */) ]), @@ -258,15 +260,15 @@ exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expr return function render(_ctx, _cache) { with (_ctx) { - const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, toDisplayString: _toDisplayString, createVNode: _createVNode } = _Vue - - return (_openBlock(), _createBlock(\\"div\\", null, [ - (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (o) => { - return (_openBlock(), _createBlock(\\"p\\", null, [ - _createVNode(\\"span\\", null, _toDisplayString(o), 1 /* TEXT */) - ])) - }), 256 /* UNKEYED_FRAGMENT */)) - ])) + const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, toDisplayString: _toDisplayString, createElementVNode: _createElementVNode } = _Vue + + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.list, (o) => { + return (_openBlock(), _createElementBlock(\\"p\\", null, [ + _createElementVNode(\\"span\\", null, _toDisplayString(o), 1 /* TEXT */, null, 9 /* ELEMENT, TEXT_CHILDREN */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) + }), 256 /* UNKEYED_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; @@ -276,13 +278,13 @@ exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist keye return function render(_ctx, _cache) { with (_ctx) { - const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createElementVNode: _createElementVNode } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ - (_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => { - return (_openBlock(), _createBlock(\\"span\\", { key: item })) - }), 128 /* KEYED_FRAGMENT */)) - ])) + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(items, (item) => { + return (_openBlock(), _createElementBlock(\\"span\\", { key: item })) + }), 128 /* KEYED_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; @@ -292,13 +294,13 @@ exports[`compiler: hoistStatic transform should NOT hoist components 1`] = ` return function render(_ctx, _cache) { with (_ctx) { - const { resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { resolveComponent: _resolveComponent, createComponentVNode: _createComponentVNode, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue const _component_Comp = _resolveComponent(\\"Comp\\") - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(_component_Comp) - ])) + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createComponentVNode(_component_Comp) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; @@ -308,11 +310,11 @@ exports[`compiler: hoistStatic transform should NOT hoist element with dynamic k return function render(_ctx, _cache) { with (_ctx) { - const { openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { openBlock: _openBlock, createElementBlock: _createElementBlock, createElementVNode: _createElementVNode } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ - (_openBlock(), _createBlock(\\"div\\", { key: foo })) - ])) + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + (_openBlock(), _createElementBlock(\\"div\\", { key: foo })) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; @@ -322,11 +324,11 @@ exports[`compiler: hoistStatic transform should NOT hoist element with dynamic p return function render(_ctx, _cache) { with (_ctx) { - const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(\\"div\\", { id: foo }, null, 8 /* PROPS */, [\\"id\\"]) - ])) + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createElementVNode(\\"div\\", { id: foo }, null, 8 /* PROPS */, [\\"id\\"]) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; @@ -336,11 +338,11 @@ exports[`compiler: hoistStatic transform should NOT hoist element with dynamic r return function render(_ctx, _cache) { with (_ctx) { - const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ - _createVNode(\\"div\\", { ref: foo }, null, 512 /* NEED_PATCH */) - ])) + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + _createElementVNode(\\"div\\", { ref: foo }, null, 512 /* NEED_PATCH */) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; @@ -350,56 +352,56 @@ exports[`compiler: hoistStatic transform should NOT hoist root node 1`] = ` return function render(_ctx, _cache) { with (_ctx) { - const { openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createBlock(\\"div\\")) + return (_openBlock(), _createElementBlock(\\"div\\")) } }" `; exports[`compiler: hoistStatic transform should hoist v-for children if static 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode } = _Vue +const { createElementVNode: _createElementVNode } = _Vue const _hoisted_1 = { id: \\"foo\\" } -const _hoisted_2 = /*#__PURE__*/_createVNode(\\"span\\", null, null, -1 /* HOISTED */) +const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"span\\", null, null, -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { - const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue + const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock, createElementVNode: _createElementVNode } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ - (_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (i) => { - return (_openBlock(), _createBlock(\\"div\\", _hoisted_1, [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (i) => { + return (_openBlock(), _createElementBlock(\\"div\\", _hoisted_1, [ _hoisted_2 - ])) - }), 256 /* UNKEYED_FRAGMENT */)) - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) + }), 256 /* UNKEYED_FRAGMENT */, null, 16 /* ARRAY_CHILDREN */)) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; exports[`compiler: hoistStatic transform should hoist v-if props/children if static 1`] = ` "const _Vue = Vue -const { createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _Vue +const { createElementVNode: _createElementVNode, createCommentVNode: _createCommentVNode } = _Vue const _hoisted_1 = { key: 0, id: \\"foo\\" } -const _hoisted_2 = /*#__PURE__*/_createVNode(\\"span\\", null, null, -1 /* HOISTED */) +const _hoisted_2 = /*#__PURE__*/_createElementVNode(\\"span\\", null, null, -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { - const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = _Vue + const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode } = _Vue - return (_openBlock(), _createBlock(\\"div\\", null, [ + return (_openBlock(), _createElementBlock(\\"div\\", null, [ ok - ? (_openBlock(), _createBlock(\\"div\\", _hoisted_1, [ + ? (_openBlock(), _createElementBlock(\\"div\\", _hoisted_1, [ _hoisted_2 - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) : _createCommentVNode(\\"v-if\\", true) - ])) + ], null, null, 17 /* ELEMENT, ARRAY_CHILDREN */)) } }" `; diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/transformExpressions.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/transformExpressions.spec.ts.snap index 165df1e1ae8..2a41250e6c4 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/transformExpressions.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/transformExpressions.spec.ts.snap @@ -2,14 +2,14 @@ exports[`compiler: expression transform bindingMetadata inline mode 1`] = ` "(_ctx, _cache) => { - return (_openBlock(), _createBlock(\\"div\\", null, _toDisplayString(__props.props) + \\" \\" + _toDisplayString(_unref(setup)) + \\" \\" + _toDisplayString(setupConst) + \\" \\" + _toDisplayString(_ctx.data) + \\" \\" + _toDisplayString(_ctx.options), 1 /* TEXT */)) + return (_openBlock(), _createElementBlock(\\"div\\", null, _toDisplayString(__props.props) + \\" \\" + _toDisplayString(_unref(setup)) + \\" \\" + _toDisplayString(setupConst) + \\" \\" + _toDisplayString(_ctx.data) + \\" \\" + _toDisplayString(_ctx.options), 1 /* TEXT */, null, 9 /* ELEMENT, TEXT_CHILDREN */)) }" `; exports[`compiler: expression transform bindingMetadata non-inline mode 1`] = ` -"const { toDisplayString: _toDisplayString, openBlock: _openBlock, createBlock: _createBlock } = Vue +"const { toDisplayString: _toDisplayString, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue return function render(_ctx, _cache, $props, $setup, $data, $options) { - return (_openBlock(), _createBlock(\\"div\\", null, _toDisplayString($props.props) + \\" \\" + _toDisplayString($setup.setup) + \\" \\" + _toDisplayString($data.data) + \\" \\" + _toDisplayString($options.options), 1 /* TEXT */)) + return (_openBlock(), _createElementBlock(\\"div\\", null, _toDisplayString($props.props) + \\" \\" + _toDisplayString($setup.setup) + \\" \\" + _toDisplayString($data.data) + \\" \\" + _toDisplayString($options.options), 1 /* TEXT */, null, 9 /* ELEMENT, TEXT_CHILDREN */)) }" `; diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/transformText.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/transformText.spec.ts.snap index 07dadbcdc64..18175530d30 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/transformText.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/transformText.spec.ts.snap @@ -5,13 +5,13 @@ exports[`compiler: transform text