diff --git a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts index 8efe67a5c..642289c29 100644 --- a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts @@ -219,11 +219,11 @@ describe('ssr: components', () => { foo: ({ list }, _push, _parent, _scopeId) => { if (_push) { if (_ctx.ok) { - _push(\`\`) + _push(\`\`) _ssrRenderList(list, (i) => { _push(\`\`) }) - _push(\`\`) + _push(\`\`) } else { _push(\`\`) } @@ -242,11 +242,11 @@ describe('ssr: components', () => { bar: ({ ok }, _push, _parent, _scopeId) => { if (_push) { if (ok) { - _push(\`\`) + _push(\`\`) _ssrRenderList(_ctx.list, (i) => { _push(\`\`) }) - _push(\`\`) + _push(\`\`) } else { _push(\`\`) } @@ -283,7 +283,7 @@ describe('ssr: components', () => { .toMatchInlineSnapshot(` " return function ssrRender(_ctx, _push, _parent) { - _push(\`
\`) + _push(\`
\`) }" `) @@ -305,7 +305,7 @@ describe('ssr: components', () => { .toMatchInlineSnapshot(` " return function ssrRender(_ctx, _push, _parent) { - _push(\`
\`) + _push(\`
\`) }" `) }) diff --git a/packages/compiler-ssr/__tests__/ssrVFor.spec.ts b/packages/compiler-ssr/__tests__/ssrVFor.spec.ts index a5b9ebd4c..eb301ab9b 100644 --- a/packages/compiler-ssr/__tests__/ssrVFor.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrVFor.spec.ts @@ -6,11 +6,9 @@ describe('ssr: v-for', () => { "const { ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _push(\`\`) _ssrRenderList(_ctx.list, (i) => { _push(\`
\`) }) - _push(\`\`) }" `) }) @@ -21,11 +19,9 @@ describe('ssr: v-for', () => { "const { ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _push(\`\`) _ssrRenderList(_ctx.list, (i) => { _push(\`
foobar
\`) }) - _push(\`\`) }" `) }) @@ -41,9 +37,8 @@ describe('ssr: v-for', () => { "const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _push(\`\`) _ssrRenderList(_ctx.list, (row, i) => { - _push(\`
\`) + _push(\`
\`) _ssrRenderList(row, (j) => { _push(\`
\${ _ssrInterpolate(i) @@ -51,9 +46,8 @@ describe('ssr: v-for', () => { _ssrInterpolate(j) }
\`) }) - _push(\`
\`) + _push(\`
\`) }) - _push(\`\`) }" `) }) @@ -64,11 +58,9 @@ describe('ssr: v-for', () => { "const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _push(\`\`) _ssrRenderList(_ctx.list, (i) => { - _push(\`\${_ssrInterpolate(i)}\`) + _push(\`\${_ssrInterpolate(i)}\`) }) - _push(\`\`) }" `) }) @@ -81,11 +73,9 @@ describe('ssr: v-for', () => { "const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _push(\`\`) _ssrRenderList(_ctx.list, (i) => { _push(\`\${_ssrInterpolate(i)}\`) }) - _push(\`\`) }" `) }) @@ -99,15 +89,13 @@ describe('ssr: v-for', () => { "const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _push(\`\`) _ssrRenderList(_ctx.list, (i) => { - _push(\`\${ + _push(\`\${ _ssrInterpolate(i) }\${ _ssrInterpolate(i + 1) - }\`) + }\`) }) - _push(\`\`) }" `) }) @@ -123,11 +111,9 @@ describe('ssr: v-for', () => { "const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _push(\`\`) _ssrRenderList(_ctx.list, ({ foo }, index) => { _push(\`
\${_ssrInterpolate(foo + _ctx.bar + index)}
\`) }) - _push(\`\`) }" `) }) diff --git a/packages/compiler-ssr/__tests__/ssrVIf.spec.ts b/packages/compiler-ssr/__tests__/ssrVIf.spec.ts index 4f9157456..8ea086797 100644 --- a/packages/compiler-ssr/__tests__/ssrVIf.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrVIf.spec.ts @@ -80,7 +80,7 @@ describe('ssr: v-if', () => { " return function ssrRender(_ctx, _push, _parent) { if (_ctx.foo) { - _push(\`hello\`) + _push(\`hello\`) } else { _push(\`\`) } @@ -110,7 +110,7 @@ describe('ssr: v-if', () => { " return function ssrRender(_ctx, _push, _parent) { if (_ctx.foo) { - _push(\`
hi
ho
\`) + _push(\`
hi
ho
\`) } else { _push(\`\`) } @@ -126,11 +126,9 @@ describe('ssr: v-if', () => { return function ssrRender(_ctx, _push, _parent) { if (_ctx.foo) { - _push(\`\`) _ssrRenderList(_ctx.list, (i) => { _push(\`
\`) }) - _push(\`\`) } else { _push(\`\`) } @@ -147,7 +145,7 @@ describe('ssr: v-if', () => { " return function ssrRender(_ctx, _push, _parent) { if (_ctx.foo) { - _push(\`
hi
ho
\`) + _push(\`
hi
ho
\`) } else { _push(\`
\`) } diff --git a/packages/compiler-ssr/src/ssrCodegenTransform.ts b/packages/compiler-ssr/src/ssrCodegenTransform.ts index 5afe4b55e..b9ef0c166 100644 --- a/packages/compiler-ssr/src/ssrCodegenTransform.ts +++ b/packages/compiler-ssr/src/ssrCodegenTransform.ts @@ -9,7 +9,6 @@ import { ElementTypes, createBlockStatement, CompilerOptions, - isText, IfStatement, CallExpression } from '@vue/compiler-dom' @@ -29,9 +28,7 @@ import { ssrProcessElement } from './transforms/ssrTransformElement' export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) { const context = createSSRTransformContext(ast, options) - const isFragment = - ast.children.length > 1 && ast.children.some(c => !isText(c)) - processChildren(ast.children, context, isFragment) + processChildren(ast.children, context) ast.codegenNode = createBlockStatement(context.body) // Finalize helpers. @@ -107,12 +104,8 @@ function createChildContext( export function processChildren( children: TemplateChildNode[], - context: SSRTransformContext, - asFragment = false + context: SSRTransformContext ) { - if (asFragment) { - context.pushStringPart(``) - } for (let i = 0; i < children.length; i++) { const child = children[i] if (child.type === NodeTypes.ELEMENT) { @@ -135,18 +128,14 @@ export function processChildren( ssrProcessFor(child, context) } } - if (asFragment) { - context.pushStringPart(``) - } } export function processChildrenAsStatement( children: TemplateChildNode[], parentContext: SSRTransformContext, - asFragment = false, withSlotScopeId = parentContext.withSlotScopeId ): BlockStatement { const childContext = createChildContext(parentContext, withSlotScopeId) - processChildren(children, childContext, asFragment) + processChildren(children, childContext) return createBlockStatement(childContext.body) } diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts index e81e13a48..5a3ad978f 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts @@ -12,8 +12,6 @@ import { FunctionExpression, TemplateChildNode, PORTAL, - SUSPENSE, - TRANSITION_GROUP, createIfStatement, createSimpleExpression, getBaseTransformPreset, @@ -135,14 +133,10 @@ export function ssrProcessComponent( // this is a built-in component that fell-through. // just render its children. const component = componentTypeMap.get(node)! - if (component === PORTAL) { return ssrProcessPortal(node, context) } - - const needFragmentWrapper = - component === SUSPENSE || component === TRANSITION_GROUP - processChildren(node.children, context, needFragmentWrapper) + processChildren(node.children, context) } else { // finish up slot function expressions from the 1st pass. const wipEntries = wipMap.get(node) || [] @@ -157,7 +151,6 @@ export function ssrProcessComponent( processChildrenAsStatement( children, context, - false, true /* withSlotScopeId */ ), vnodeBranch diff --git a/packages/compiler-ssr/src/transforms/ssrVFor.ts b/packages/compiler-ssr/src/transforms/ssrVFor.ts index 1b6034ba8..1921b8f00 100644 --- a/packages/compiler-ssr/src/transforms/ssrVFor.ts +++ b/packages/compiler-ssr/src/transforms/ssrVFor.ts @@ -4,8 +4,7 @@ import { processFor, createCallExpression, createFunctionExpression, - createForLoopParams, - NodeTypes + createForLoopParams } from '@vue/compiler-dom' import { SSRTransformContext, @@ -22,24 +21,14 @@ export const ssrTransformFor = createStructuralDirectiveTransform( // This is called during the 2nd transform pass to construct the SSR-sepcific // codegen nodes. export function ssrProcessFor(node: ForNode, context: SSRTransformContext) { - const needFragmentWrapper = - node.children.length !== 1 || node.children[0].type !== NodeTypes.ELEMENT const renderLoop = createFunctionExpression( createForLoopParams(node.parseResult) ) - renderLoop.body = processChildrenAsStatement( - node.children, - context, - needFragmentWrapper - ) - - // v-for always renders a fragment - context.pushStringPart(``) + renderLoop.body = processChildrenAsStatement(node.children, context) context.pushStatement( createCallExpression(context.helper(SSR_RENDER_LIST), [ node.source, renderLoop ]) ) - context.pushStringPart(``) } diff --git a/packages/compiler-ssr/src/transforms/ssrVIf.ts b/packages/compiler-ssr/src/transforms/ssrVIf.ts index aad7ad14d..d1c71e1d5 100644 --- a/packages/compiler-ssr/src/transforms/ssrVIf.ts +++ b/packages/compiler-ssr/src/transforms/ssrVIf.ts @@ -4,10 +4,7 @@ import { IfNode, createIfStatement, createBlockStatement, - createCallExpression, - IfBranchNode, - BlockStatement, - NodeTypes + createCallExpression } from '@vue/compiler-dom' import { SSRTransformContext, @@ -26,14 +23,17 @@ export function ssrProcessIf(node: IfNode, context: SSRTransformContext) { const [rootBranch] = node.branches const ifStatement = createIfStatement( rootBranch.condition!, - processIfBranch(rootBranch, context) + processChildrenAsStatement(rootBranch.children, context) ) context.pushStatement(ifStatement) let currentIf = ifStatement for (let i = 1; i < node.branches.length; i++) { const branch = node.branches[i] - const branchBlockStatement = processIfBranch(branch, context) + const branchBlockStatement = processChildrenAsStatement( + branch.children, + context + ) if (branch.condition) { // else-if currentIf = currentIf.alternate = createIfStatement( @@ -52,15 +52,3 @@ export function ssrProcessIf(node: IfNode, context: SSRTransformContext) { ]) } } - -function processIfBranch( - branch: IfBranchNode, - context: SSRTransformContext -): BlockStatement { - const { children } = branch - const needFragmentWrapper = - (children.length !== 1 || children[0].type !== NodeTypes.ELEMENT) && - // optimize away nested fragments when the only child is a ForNode - !(children.length === 1 && children[0].type === NodeTypes.FOR) - return processChildrenAsStatement(children, context, needFragmentWrapper) -} diff --git a/packages/runtime-core/__tests__/apiOptions.spec.ts b/packages/runtime-core/__tests__/apiOptions.spec.ts index a7d5ca6b9..36f361740 100644 --- a/packages/runtime-core/__tests__/apiOptions.spec.ts +++ b/packages/runtime-core/__tests__/apiOptions.spec.ts @@ -281,7 +281,7 @@ describe('api: options', () => { } } as any - expect(renderToString(h(Root))).toBe(`1112`) + expect(renderToString(h(Root))).toBe(`1112`) }) test('lifecycle', async () => { diff --git a/packages/runtime-core/__tests__/components/Portal.spec.ts b/packages/runtime-core/__tests__/components/Portal.spec.ts index b71d5d6fb..52e1676a5 100644 --- a/packages/runtime-core/__tests__/components/Portal.spec.ts +++ b/packages/runtime-core/__tests__/components/Portal.spec.ts @@ -6,7 +6,6 @@ import { defineComponent, Portal, Text, - Fragment, ref, nextTick, TestElement, @@ -19,12 +18,10 @@ describe('renderer: portal', () => { const target = nodeOps.createElement('div') const root = nodeOps.createElement('div') - const Comp = defineComponent(() => () => - h(Fragment, [ - h(Portal, { target }, h('div', 'teleported')), - h('div', 'root') - ]) - ) + const Comp = defineComponent(() => () => [ + h(Portal, { target }, h('div', 'teleported')), + h('div', 'root') + ]) render(h(Comp), root) expect(serializeInner(root)).toMatchSnapshot() @@ -37,12 +34,10 @@ describe('renderer: portal', () => { const target = ref(targetA) const root = nodeOps.createElement('div') - const Comp = defineComponent(() => () => - h(Fragment, [ - h(Portal, { target: target.value }, h('div', 'teleported')), - h('div', 'root') - ]) - ) + const Comp = defineComponent(() => () => [ + h(Portal, { target: target.value }, h('div', 'teleported')), + h('div', 'root') + ]) render(h(Comp), root) expect(serializeInner(root)).toMatchSnapshot() diff --git a/packages/runtime-core/__tests__/components/Suspense.spec.ts b/packages/runtime-core/__tests__/components/Suspense.spec.ts index 6a115e39c..119144068 100644 --- a/packages/runtime-core/__tests__/components/Suspense.spec.ts +++ b/packages/runtime-core/__tests__/components/Suspense.spec.ts @@ -451,14 +451,14 @@ describe('Suspense', () => { await deps[0] await nextTick() expect(serializeInner(root)).toBe( - `
async outer
fallback inner
` + `
async outer
fallback inner
` ) expect(calls).toEqual([`outer mounted`]) await Promise.all(deps) await nextTick() expect(serializeInner(root)).toBe( - `
async outer
async inner
` + `
async outer
async inner
` ) expect(calls).toEqual([`outer mounted`, `inner mounted`]) }) @@ -522,7 +522,7 @@ describe('Suspense', () => { await Promise.all(deps) await nextTick() expect(serializeInner(root)).toBe( - `
async outer
async inner
` + `
async outer
async inner
` ) expect(calls).toEqual([`inner mounted`, `outer mounted`]) }) @@ -663,7 +663,7 @@ describe('Suspense', () => { await deps[3] await nextTick() expect(serializeInner(root)).toBe( - `
nested fallback
root async
` + `
nested fallback
root async
` ) expect(calls).toEqual([0, 1, 3]) @@ -674,7 +674,7 @@ describe('Suspense', () => { await Promise.all(deps) await nextTick() expect(serializeInner(root)).toBe( - `
nested changed
root async
` + `
nested changed
root async
` ) expect(calls).toEqual([0, 1, 3, 2]) @@ -682,7 +682,7 @@ describe('Suspense', () => { msg.value = 'nested changed again' await nextTick() expect(serializeInner(root)).toBe( - `
nested changed again
root async
` + `
nested changed again
root async
` ) }) @@ -717,7 +717,7 @@ describe('Suspense', () => { await deps[0] await nextTick() - expect(serializeInner(root)).toBe(`
Child A
`) + expect(serializeInner(root)).toBe(`
Child A
`) toggle.value = true await nextTick() @@ -725,9 +725,7 @@ describe('Suspense', () => { await deps[1] await nextTick() - expect(serializeInner(root)).toBe( - `
Child A
Child B
` - ) + expect(serializeInner(root)).toBe(`
Child A
Child B
`) }) test.todo('portal inside suspense') diff --git a/packages/runtime-core/__tests__/components/__snapshots__/Portal.spec.ts.snap b/packages/runtime-core/__tests__/components/__snapshots__/Portal.spec.ts.snap index 77d48c3cc..4a47a5858 100644 --- a/packages/runtime-core/__tests__/components/__snapshots__/Portal.spec.ts.snap +++ b/packages/runtime-core/__tests__/components/__snapshots__/Portal.spec.ts.snap @@ -6,18 +6,18 @@ exports[`renderer: portal should update children 2`] = `""`; exports[`renderer: portal should update children 3`] = `"teleported"`; -exports[`renderer: portal should update target 1`] = `"
root
"`; +exports[`renderer: portal should update target 1`] = `"
root
"`; exports[`renderer: portal should update target 2`] = `"
teleported
"`; exports[`renderer: portal should update target 3`] = `""`; -exports[`renderer: portal should update target 4`] = `"
root
"`; +exports[`renderer: portal should update target 4`] = `"
root
"`; exports[`renderer: portal should update target 5`] = `""`; exports[`renderer: portal should update target 6`] = `"
teleported
"`; -exports[`renderer: portal should work 1`] = `"
root
"`; +exports[`renderer: portal should work 1`] = `"
root
"`; exports[`renderer: portal should work 2`] = `"
teleported
"`; diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index 4da8d0db4..08b734fa3 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -60,13 +60,13 @@ describe('hot module replacement', () => { createRecord(parentId, Parent) render(h(Parent), root) - expect(serializeInner(root)).toBe(`
00
`) + expect(serializeInner(root)).toBe(`
00
`) // Perform some state change. This change should be preserved after the // re-render! triggerEvent(root.children[0] as TestElement, 'click') await nextTick() - expect(serializeInner(root)).toBe(`
11
`) + expect(serializeInner(root)).toBe(`
11
`) // Update text while preserving state rerender( @@ -75,7 +75,7 @@ describe('hot module replacement', () => { `
{{ count }}!{{ count }}
` ) ) - expect(serializeInner(root)).toBe(`
1!1
`) + expect(serializeInner(root)).toBe(`
1!1
`) // Should force child update on slot content change rerender( @@ -84,7 +84,7 @@ describe('hot module replacement', () => { `
{{ count }}!{{ count }}!
` ) ) - expect(serializeInner(root)).toBe(`
1!1!
`) + expect(serializeInner(root)).toBe(`
1!1!
`) // Should force update element children despite block optimization rerender( @@ -95,9 +95,7 @@ describe('hot module replacement', () => { ` ) ) - expect(serializeInner(root)).toBe( - `
111!
` - ) + expect(serializeInner(root)).toBe(`
111!
`) // Should force update child slot elements rerender( @@ -108,7 +106,7 @@ describe('hot module replacement', () => { ` ) ) - expect(serializeInner(root)).toBe(`
1
`) + expect(serializeInner(root)).toBe(`
1
`) }) test('reload', async () => { diff --git a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts index fe34eee60..136a01256 100644 --- a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts +++ b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts @@ -322,9 +322,7 @@ describe('attribute fallthrough', () => { render(h(Parent), root) expect(`Extraneous non-props attributes`).not.toHaveBeenWarned() - expect(root.innerHTML).toBe( - `
` - ) + expect(root.innerHTML).toBe(`
`) }) it('should not warn when context.attrs is used during render', () => { @@ -346,8 +344,6 @@ describe('attribute fallthrough', () => { render(h(Parent), root) expect(`Extraneous non-props attributes`).not.toHaveBeenWarned() - expect(root.innerHTML).toBe( - `
` - ) + expect(root.innerHTML).toBe(`
`) }) }) diff --git a/packages/runtime-core/__tests__/rendererFragment.spec.ts b/packages/runtime-core/__tests__/rendererFragment.spec.ts index 091ef7a29..687bcbab2 100644 --- a/packages/runtime-core/__tests__/rendererFragment.spec.ts +++ b/packages/runtime-core/__tests__/rendererFragment.spec.ts @@ -25,10 +25,11 @@ describe('renderer: fragment', () => { const root = nodeOps.createElement('div') render(h(App), root) - expect(serializeInner(root)).toBe(`
one
two`) + expect(serializeInner(root)).toBe(`
one
two`) expect(root.children.length).toBe(4) expect(root.children[0]).toMatchObject({ - type: NodeTypes.COMMENT + type: NodeTypes.TEXT, + text: '' }) expect(root.children[1]).toMatchObject({ type: NodeTypes.ELEMENT, @@ -43,7 +44,8 @@ describe('renderer: fragment', () => { text: 'two' }) expect(root.children[3]).toMatchObject({ - type: NodeTypes.COMMENT + type: NodeTypes.TEXT, + text: '' }) }) @@ -51,7 +53,7 @@ describe('renderer: fragment', () => { const root = nodeOps.createElement('div') render(h('div', [h(Fragment, [h('div', 'one'), 'two'])]), root) const parent = root.children[0] as TestElement - expect(serializeInner(parent)).toBe(`
one
two`) + expect(serializeInner(parent)).toBe(`
one
two`) }) it('patch fragment children (manual, keyed)', () => { @@ -60,18 +62,14 @@ describe('renderer: fragment', () => { h(Fragment, [h('div', { key: 1 }, 'one'), h('div', { key: 2 }, 'two')]), root ) - expect(serializeInner(root)).toBe( - `
one
two
` - ) + expect(serializeInner(root)).toBe(`
one
two
`) resetOps() render( h(Fragment, [h('div', { key: 2 }, 'two'), h('div', { key: 1 }, 'one')]), root ) - expect(serializeInner(root)).toBe( - `
two
one
` - ) + expect(serializeInner(root)).toBe(`
two
one
`) const ops = dumpOps() // should be moving nodes instead of re-creating or patching them expect(ops).toMatchObject([ @@ -84,15 +82,11 @@ describe('renderer: fragment', () => { it('patch fragment children (manual, unkeyed)', () => { const root = nodeOps.createElement('div') render(h(Fragment, [h('div', 'one'), h('div', 'two')]), root) - expect(serializeInner(root)).toBe( - `
one
two
` - ) + expect(serializeInner(root)).toBe(`
one
two
`) resetOps() render(h(Fragment, [h('div', 'two'), h('div', 'one')]), root) - expect(serializeInner(root)).toBe( - `
two
one
` - ) + expect(serializeInner(root)).toBe(`
two
one
`) const ops = dumpOps() // should be patching nodes instead of moving or re-creating them expect(ops).toMatchObject([ @@ -119,7 +113,7 @@ describe('renderer: fragment', () => { ), root ) - expect(serializeInner(root)).toBe(`
one
two`) + expect(serializeInner(root)).toBe(`
one
two`) render( createVNode( @@ -134,7 +128,7 @@ describe('renderer: fragment', () => { ), root ) - expect(serializeInner(root)).toBe(`
foo
barbaz`) + expect(serializeInner(root)).toBe(`
foo
barbaz`) }) it('patch fragment children (compiler generated, keyed)', () => { @@ -149,9 +143,7 @@ describe('renderer: fragment', () => { ), root ) - expect(serializeInner(root)).toBe( - `
one
two
` - ) + expect(serializeInner(root)).toBe(`
one
two
`) resetOps() render( @@ -163,9 +155,7 @@ describe('renderer: fragment', () => { ), root ) - expect(serializeInner(root)).toBe( - `
two
one
` - ) + expect(serializeInner(root)).toBe(`
two
one
`) const ops = dumpOps() // should be moving nodes instead of re-creating or patching them expect(ops).toMatchObject([ @@ -188,7 +178,7 @@ describe('renderer: fragment', () => { root ) expect(serializeInner(root)).toBe( - `
outer
one
two
` + `
outer
one
two
` ) resetOps() @@ -203,7 +193,7 @@ describe('renderer: fragment', () => { root ) expect(serializeInner(root)).toBe( - `
two
one
outer
` + `
two
one
outer
` ) const ops = dumpOps() // should be moving nodes instead of re-creating them @@ -213,10 +203,10 @@ describe('renderer: fragment', () => { // 2. move entire fragment, including anchors // not the most efficient move, but this case is super rare // and optimizing for this special case complicates the algo quite a bit - { type: NodeOpTypes.INSERT, targetNode: { type: 'comment' } }, + { type: NodeOpTypes.INSERT, targetNode: { type: 'text', text: '' } }, { type: NodeOpTypes.INSERT, targetNode: { type: 'element' } }, { type: NodeOpTypes.INSERT, targetNode: { type: 'element' } }, - { type: NodeOpTypes.INSERT, targetNode: { type: 'comment' } } + { type: NodeOpTypes.INSERT, targetNode: { type: 'text', text: '' } } ]) }) @@ -234,7 +224,7 @@ describe('renderer: fragment', () => { root ) expect(serializeInner(root)).toBe( - `
outer
one
two
` + `
outer
one
two
` ) resetOps() @@ -249,16 +239,16 @@ describe('renderer: fragment', () => { root ) expect(serializeInner(root)).toBe( - `
two
one
outer
` + `
two
one
outer
` ) const ops = dumpOps() // should be moving nodes instead of re-creating them expect(ops).toMatchObject([ { type: NodeOpTypes.INSERT, targetNode: { type: 'element' } }, - { type: NodeOpTypes.INSERT, targetNode: { type: 'comment' } }, + { type: NodeOpTypes.INSERT, targetNode: { type: 'text', text: '' } }, { type: NodeOpTypes.INSERT, targetNode: { type: 'element' } }, { type: NodeOpTypes.INSERT, targetNode: { type: 'element' } }, - { type: NodeOpTypes.INSERT, targetNode: { type: 'comment' } } + { type: NodeOpTypes.INSERT, targetNode: { type: 'text', text: '' } } ]) // should properly remove nested fragments diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 8861a1984..ffe71ad36 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -24,7 +24,7 @@ export type RootHydrateFunction = ( // passed in via arguments. export function createHydrationFunctions({ mt: mountComponent, - o: { patchProp } + o: { patchProp, createText } }: RendererInternals) { const hydrate: RootHydrateFunction = (vnode, container) => { if (__DEV__ && !container.hasChildNodes()) { @@ -40,7 +40,7 @@ export function createHydrationFunctions({ node: Node, vnode: VNode, parentComponent: ComponentInternalInstance | null = null - ): Node | null | undefined => { + ): Node | null => { const { type, shapeFlag } = vnode vnode.el = node switch (type) { @@ -49,14 +49,15 @@ export function createHydrationFunctions({ case Static: return node.nextSibling case Fragment: - const anchor = (vnode.anchor = hydrateChildren( - node.nextSibling, + const parent = node.parentNode! + parent.insertBefore((vnode.el = createText('')), node) + const next = hydrateChildren( + node, vnode.children as VNode[], parentComponent - )!) - // TODO handle potential hydration error if fragment didn't get - // back anchor as expected. - return anchor.nextSibling + ) + parent.insertBefore((vnode.anchor = createText('')), next) + return next default: if (shapeFlag & ShapeFlags.ELEMENT) { return hydrateElement(node as Element, vnode, parentComponent) @@ -75,6 +76,7 @@ export function createHydrationFunctions({ } else if (__DEV__) { warn('Invalid HostVNode type:', type, `(${typeof type})`) } + return null } } @@ -130,10 +132,10 @@ export function createHydrationFunctions({ } const hydrateChildren = ( - node: Node | null | undefined, + node: Node | null, vnodes: VNode[], parentComponent: ComponentInternalInstance | null - ): Node | null | undefined => { + ): Node | null => { for (let i = 0; node != null && i < vnodes.length; i++) { // TODO can skip normalizeVNode in optimized mode // (need hint on rendered markup?) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index a92708633..3e0cef8c1 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -126,7 +126,6 @@ export interface RendererInternals { pbc: PatchBlockChildrenFn n: NextFn o: RendererOptions - c: ProcessTextOrCommentFn } // These functions are created inside a closure and therefore their types cannot @@ -845,8 +844,6 @@ function baseCreateRenderer< } } - let devFragmentID = 0 - const processFragment = ( n1: HostVNode | null, n2: HostVNode, @@ -857,13 +854,8 @@ function baseCreateRenderer< isSVG: boolean, optimized: boolean ) => { - const showID = __DEV__ && !__TEST__ - const fragmentStartAnchor = (n2.el = n1 - ? n1.el - : hostCreateComment(showID ? `fragment-${devFragmentID}-start` : ''))! - const fragmentEndAnchor = (n2.anchor = n1 - ? n1.anchor - : hostCreateComment(showID ? `fragment-${devFragmentID}-end` : ''))! + const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateText(''))! + const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateText(''))! let { patchFlag, dynamicChildren } = n2 if (patchFlag > 0) { @@ -878,9 +870,6 @@ function baseCreateRenderer< } if (n1 == null) { - if (showID) { - devFragmentID++ - } hostInsert(fragmentStartAnchor, container, anchor) hostInsert(fragmentEndAnchor, container, anchor) // a fragment can only have array children @@ -1864,7 +1853,6 @@ function baseCreateRenderer< pc: patchChildren, pbc: patchBlockChildren, n: getNextHostNode, - c: processCommentNode, o: options } diff --git a/packages/server-renderer/__tests__/renderToString.spec.ts b/packages/server-renderer/__tests__/renderToString.spec.ts index c8bb79c87..730f7a145 100644 --- a/packages/server-renderer/__tests__/renderToString.spec.ts +++ b/packages/server-renderer/__tests__/renderToString.spec.ts @@ -262,7 +262,7 @@ describe('ssr: renderToString', () => { ) ).toBe( `
parent
` + - `from slot` + + `from slot` + `
` ) @@ -277,7 +277,7 @@ describe('ssr: renderToString', () => { } }) ) - ).toBe(`
parent
fallback
`) + ).toBe(`
parent
fallback
`) }) test('nested components with vnode slots', async () => { @@ -321,7 +321,7 @@ describe('ssr: renderToString', () => { ) ).toBe( `
parent
` + - `from slot` + + `from slot` + `
` ) }) @@ -339,7 +339,7 @@ describe('ssr: renderToString', () => { expect(await renderToString(app)).toBe( `
parent
` + - `from slot` + + `from slot` + `
` ) }) @@ -461,9 +461,7 @@ describe('ssr: renderToString', () => { createCommentVNode('qux') ]) ) - ).toBe( - `
foobarbaz
` - ) + ).toBe(`
foobarbaz
`) }) test('void elements', async () => { diff --git a/packages/server-renderer/src/helpers/ssrRenderSlot.ts b/packages/server-renderer/src/helpers/ssrRenderSlot.ts index d8826a03e..1aae61f2b 100644 --- a/packages/server-renderer/src/helpers/ssrRenderSlot.ts +++ b/packages/server-renderer/src/helpers/ssrRenderSlot.ts @@ -19,8 +19,6 @@ export function ssrRenderSlot( parentComponent: ComponentInternalInstance ) { const slotFn = slots[slotName] - // template-compiled slots are always rendered as fragments - push(``) if (slotFn) { if (slotFn.length > 1) { // only ssr-optimized slot fns accept more than 1 arguments @@ -33,5 +31,4 @@ export function ssrRenderSlot( } else if (fallbackRenderFn) { fallbackRenderFn() } - push(``) } diff --git a/packages/server-renderer/src/renderToString.ts b/packages/server-renderer/src/renderToString.ts index 14c041c65..5b5ecc4d5 100644 --- a/packages/server-renderer/src/renderToString.ts +++ b/packages/server-renderer/src/renderToString.ts @@ -238,9 +238,7 @@ function renderVNode( push(children ? `` : ``) break case Fragment: - push(``) renderVNodeChildren(push, children as VNodeArrayChildren, parentComponent) - push(``) break default: if (shapeFlag & ShapeFlags.ELEMENT) {