refactor(fragments): remove visible anchors for fragments
This commit is contained in:
parent
439752822c
commit
11d2fb2594
@ -219,11 +219,11 @@ describe('ssr: components', () => {
|
||||
foo: ({ list }, _push, _parent, _scopeId) => {
|
||||
if (_push) {
|
||||
if (_ctx.ok) {
|
||||
_push(\`<div\${_scopeId}><!---->\`)
|
||||
_push(\`<div\${_scopeId}>\`)
|
||||
_ssrRenderList(list, (i) => {
|
||||
_push(\`<span\${_scopeId}></span>\`)
|
||||
})
|
||||
_push(\`<!----></div>\`)
|
||||
_push(\`</div>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
@ -242,11 +242,11 @@ describe('ssr: components', () => {
|
||||
bar: ({ ok }, _push, _parent, _scopeId) => {
|
||||
if (_push) {
|
||||
if (ok) {
|
||||
_push(\`<div\${_scopeId}><!---->\`)
|
||||
_push(\`<div\${_scopeId}>\`)
|
||||
_ssrRenderList(_ctx.list, (i) => {
|
||||
_push(\`<span\${_scopeId}></span>\`)
|
||||
})
|
||||
_push(\`<!----></div>\`)
|
||||
_push(\`</div>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
@ -283,7 +283,7 @@ describe('ssr: components', () => {
|
||||
.toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
_push(\`<!----><div></div><!---->\`)
|
||||
_push(\`<div></div>\`)
|
||||
}"
|
||||
`)
|
||||
|
||||
@ -305,7 +305,7 @@ describe('ssr: components', () => {
|
||||
.toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
_push(\`<!----><div></div><!---->\`)
|
||||
_push(\`<div></div>\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
@ -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(\`<div></div>\`)
|
||||
})
|
||||
_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(\`<div>foo<span>bar</span></div>\`)
|
||||
})
|
||||
_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(\`<div><!---->\`)
|
||||
_push(\`<div>\`)
|
||||
_ssrRenderList(row, (j) => {
|
||||
_push(\`<div>\${
|
||||
_ssrInterpolate(i)
|
||||
@ -51,9 +46,8 @@ describe('ssr: v-for', () => {
|
||||
_ssrInterpolate(j)
|
||||
}</div>\`)
|
||||
})
|
||||
_push(\`<!----></div>\`)
|
||||
_push(\`</div>\`)
|
||||
})
|
||||
_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(\`<span>\${_ssrInterpolate(i)}</span>\`)
|
||||
})
|
||||
_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(\`<!----><span>\${
|
||||
_push(\`<span>\${
|
||||
_ssrInterpolate(i)
|
||||
}</span><span>\${
|
||||
_ssrInterpolate(i + 1)
|
||||
}</span><!---->\`)
|
||||
}</span>\`)
|
||||
})
|
||||
_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(\`<div>\${_ssrInterpolate(foo + _ctx.bar + index)}</div>\`)
|
||||
})
|
||||
_push(\`<!---->\`)
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
@ -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(\`<!----><div>hi</div><div>ho</div><!---->\`)
|
||||
_push(\`<div>hi</div><div>ho</div>\`)
|
||||
} 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(\`<div></div>\`)
|
||||
})
|
||||
_push(\`<!---->\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
@ -147,7 +145,7 @@ describe('ssr: v-if', () => {
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<!----><div>hi</div><div>ho</div><!---->\`)
|
||||
_push(\`<div>hi</div><div>ho</div>\`)
|
||||
} else {
|
||||
_push(\`<div></div>\`)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(`<!---->`)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -281,7 +281,7 @@ describe('api: options', () => {
|
||||
}
|
||||
} as any
|
||||
|
||||
expect(renderToString(h(Root))).toBe(`<!---->1112<!---->`)
|
||||
expect(renderToString(h(Root))).toBe(`1112`)
|
||||
})
|
||||
|
||||
test('lifecycle', async () => {
|
||||
|
@ -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, [
|
||||
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, [
|
||||
const Comp = defineComponent(() => () => [
|
||||
h(Portal, { target: target.value }, h('div', 'teleported')),
|
||||
h('div', 'root')
|
||||
])
|
||||
)
|
||||
render(h(Comp), root)
|
||||
|
||||
expect(serializeInner(root)).toMatchSnapshot()
|
||||
|
@ -451,14 +451,14 @@ describe('Suspense', () => {
|
||||
await deps[0]
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>async outer</div><div>fallback inner</div><!---->`
|
||||
`<div>async outer</div><div>fallback inner</div>`
|
||||
)
|
||||
expect(calls).toEqual([`outer mounted`])
|
||||
|
||||
await Promise.all(deps)
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>async outer</div><div>async inner</div><!---->`
|
||||
`<div>async outer</div><div>async inner</div>`
|
||||
)
|
||||
expect(calls).toEqual([`outer mounted`, `inner mounted`])
|
||||
})
|
||||
@ -522,7 +522,7 @@ describe('Suspense', () => {
|
||||
await Promise.all(deps)
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>async outer</div><div>async inner</div><!---->`
|
||||
`<div>async outer</div><div>async inner</div>`
|
||||
)
|
||||
expect(calls).toEqual([`inner mounted`, `outer mounted`])
|
||||
})
|
||||
@ -663,7 +663,7 @@ describe('Suspense', () => {
|
||||
await deps[3]
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>nested fallback</div><div>root async</div><!---->`
|
||||
`<div>nested fallback</div><div>root async</div>`
|
||||
)
|
||||
expect(calls).toEqual([0, 1, 3])
|
||||
|
||||
@ -674,7 +674,7 @@ describe('Suspense', () => {
|
||||
await Promise.all(deps)
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>nested changed</div><div>root async</div><!---->`
|
||||
`<div>nested changed</div><div>root async</div>`
|
||||
)
|
||||
expect(calls).toEqual([0, 1, 3, 2])
|
||||
|
||||
@ -682,7 +682,7 @@ describe('Suspense', () => {
|
||||
msg.value = 'nested changed again'
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>nested changed again</div><div>root async</div><!---->`
|
||||
`<div>nested changed again</div><div>root async</div>`
|
||||
)
|
||||
})
|
||||
|
||||
@ -717,7 +717,7 @@ describe('Suspense', () => {
|
||||
|
||||
await deps[0]
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(`<!----><div>Child A</div><!----><!---->`)
|
||||
expect(serializeInner(root)).toBe(`<div>Child A</div><!---->`)
|
||||
|
||||
toggle.value = true
|
||||
await nextTick()
|
||||
@ -725,9 +725,7 @@ describe('Suspense', () => {
|
||||
|
||||
await deps[1]
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>Child A</div><div>Child B</div><!---->`
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>Child A</div><div>Child B</div>`)
|
||||
})
|
||||
|
||||
test.todo('portal inside suspense')
|
||||
|
@ -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`] = `"<!----><!--[object Object]--><div>root</div><!---->"`;
|
||||
exports[`renderer: portal should update target 1`] = `"<!--portal--><div>root</div>"`;
|
||||
|
||||
exports[`renderer: portal should update target 2`] = `"<div>teleported</div>"`;
|
||||
|
||||
exports[`renderer: portal should update target 3`] = `""`;
|
||||
|
||||
exports[`renderer: portal should update target 4`] = `"<!----><!--[object Object]--><div>root</div><!---->"`;
|
||||
exports[`renderer: portal should update target 4`] = `"<!--portal--><div>root</div>"`;
|
||||
|
||||
exports[`renderer: portal should update target 5`] = `""`;
|
||||
|
||||
exports[`renderer: portal should update target 6`] = `"<div>teleported</div>"`;
|
||||
|
||||
exports[`renderer: portal should work 1`] = `"<!----><!--[object Object]--><div>root</div><!---->"`;
|
||||
exports[`renderer: portal should work 1`] = `"<!--portal--><div>root</div>"`;
|
||||
|
||||
exports[`renderer: portal should work 2`] = `"<div>teleported</div>"`;
|
||||
|
@ -60,13 +60,13 @@ describe('hot module replacement', () => {
|
||||
createRecord(parentId, Parent)
|
||||
|
||||
render(h(Parent), root)
|
||||
expect(serializeInner(root)).toBe(`<div>0<!---->0<!----></div>`)
|
||||
expect(serializeInner(root)).toBe(`<div>00</div>`)
|
||||
|
||||
// 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(`<div>1<!---->1<!----></div>`)
|
||||
expect(serializeInner(root)).toBe(`<div>11</div>`)
|
||||
|
||||
// Update text while preserving state
|
||||
rerender(
|
||||
@ -75,7 +75,7 @@ describe('hot module replacement', () => {
|
||||
`<div @click="count++">{{ count }}!<Child>{{ count }}</Child></div>`
|
||||
)
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>1!<!---->1<!----></div>`)
|
||||
expect(serializeInner(root)).toBe(`<div>1!1</div>`)
|
||||
|
||||
// Should force child update on slot content change
|
||||
rerender(
|
||||
@ -84,7 +84,7 @@ describe('hot module replacement', () => {
|
||||
`<div @click="count++">{{ count }}!<Child>{{ count }}!</Child></div>`
|
||||
)
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>1!<!---->1!<!----></div>`)
|
||||
expect(serializeInner(root)).toBe(`<div>1!1!</div>`)
|
||||
|
||||
// Should force update element children despite block optimization
|
||||
rerender(
|
||||
@ -95,9 +95,7 @@ describe('hot module replacement', () => {
|
||||
</div>`
|
||||
)
|
||||
)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<div>1<span>1</span><!---->1!<!----></div>`
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>1<span>1</span>1!</div>`)
|
||||
|
||||
// Should force update child slot elements
|
||||
rerender(
|
||||
@ -108,7 +106,7 @@ describe('hot module replacement', () => {
|
||||
</div>`
|
||||
)
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div><!----><span>1</span><!----></div>`)
|
||||
expect(serializeInner(root)).toBe(`<div><span>1</span></div>`)
|
||||
})
|
||||
|
||||
test('reload', async () => {
|
||||
|
@ -322,9 +322,7 @@ describe('attribute fallthrough', () => {
|
||||
render(h(Parent), root)
|
||||
|
||||
expect(`Extraneous non-props attributes`).not.toHaveBeenWarned()
|
||||
expect(root.innerHTML).toBe(
|
||||
`<!----><div></div><div class="parent"></div><!---->`
|
||||
)
|
||||
expect(root.innerHTML).toBe(`<div></div><div class="parent"></div>`)
|
||||
})
|
||||
|
||||
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(
|
||||
`<!----><div></div><div class="parent"></div><!---->`
|
||||
)
|
||||
expect(root.innerHTML).toBe(`<div></div><div class="parent"></div>`)
|
||||
})
|
||||
})
|
||||
|
@ -25,10 +25,11 @@ describe('renderer: fragment', () => {
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(App), root)
|
||||
|
||||
expect(serializeInner(root)).toBe(`<!----><div>one</div>two<!---->`)
|
||||
expect(serializeInner(root)).toBe(`<div>one</div>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(`<!----><div>one</div>two<!---->`)
|
||||
expect(serializeInner(parent)).toBe(`<div>one</div>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(
|
||||
`<!----><div>one</div><div>two</div><!---->`
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>one</div><div>two</div>`)
|
||||
|
||||
resetOps()
|
||||
render(
|
||||
h(Fragment, [h('div', { key: 2 }, 'two'), h('div', { key: 1 }, 'one')]),
|
||||
root
|
||||
)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>two</div><div>one</div><!---->`
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>two</div><div>one</div>`)
|
||||
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(
|
||||
`<!----><div>one</div><div>two</div><!---->`
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>one</div><div>two</div>`)
|
||||
|
||||
resetOps()
|
||||
render(h(Fragment, [h('div', 'two'), h('div', 'one')]), root)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>two</div><div>one</div><!---->`
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>two</div><div>one</div>`)
|
||||
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(`<!----><div>one</div>two<!---->`)
|
||||
expect(serializeInner(root)).toBe(`<div>one</div>two`)
|
||||
|
||||
render(
|
||||
createVNode(
|
||||
@ -134,7 +128,7 @@ describe('renderer: fragment', () => {
|
||||
),
|
||||
root
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<!----><div>foo</div>barbaz<!---->`)
|
||||
expect(serializeInner(root)).toBe(`<div>foo</div>barbaz`)
|
||||
})
|
||||
|
||||
it('patch fragment children (compiler generated, keyed)', () => {
|
||||
@ -149,9 +143,7 @@ describe('renderer: fragment', () => {
|
||||
),
|
||||
root
|
||||
)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>one</div><div>two</div><!---->`
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>one</div><div>two</div>`)
|
||||
|
||||
resetOps()
|
||||
render(
|
||||
@ -163,9 +155,7 @@ describe('renderer: fragment', () => {
|
||||
),
|
||||
root
|
||||
)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><div>two</div><div>one</div><!---->`
|
||||
)
|
||||
expect(serializeInner(root)).toBe(`<div>two</div><div>one</div>`)
|
||||
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(
|
||||
`<div><div>outer</div><!----><div>one</div><div>two</div><!----></div>`
|
||||
`<div><div>outer</div><div>one</div><div>two</div></div>`
|
||||
)
|
||||
|
||||
resetOps()
|
||||
@ -203,7 +193,7 @@ describe('renderer: fragment', () => {
|
||||
root
|
||||
)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<div><!----><div>two</div><div>one</div><!----><div>outer</div></div>`
|
||||
`<div><div>two</div><div>one</div><div>outer</div></div>`
|
||||
)
|
||||
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(
|
||||
`<!----><div>outer</div><!----><div>one</div><div>two</div><!----><!---->`
|
||||
`<div>outer</div><div>one</div><div>two</div>`
|
||||
)
|
||||
|
||||
resetOps()
|
||||
@ -249,16 +239,16 @@ describe('renderer: fragment', () => {
|
||||
root
|
||||
)
|
||||
expect(serializeInner(root)).toBe(
|
||||
`<!----><!----><div>two</div><div>one</div><!----><div>outer</div><!---->`
|
||||
`<div>two</div><div>one</div><div>outer</div>`
|
||||
)
|
||||
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
|
||||
|
@ -24,7 +24,7 @@ export type RootHydrateFunction = (
|
||||
// passed in via arguments.
|
||||
export function createHydrationFunctions({
|
||||
mt: mountComponent,
|
||||
o: { patchProp }
|
||||
o: { patchProp, createText }
|
||||
}: RendererInternals<Node, Element>) {
|
||||
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?)
|
||||
|
@ -126,7 +126,6 @@ export interface RendererInternals<HostNode = any, HostElement = any> {
|
||||
pbc: PatchBlockChildrenFn<HostNode, HostElement>
|
||||
n: NextFn<HostNode, HostElement>
|
||||
o: RendererOptions<HostNode, HostElement>
|
||||
c: ProcessTextOrCommentFn<HostNode, HostElement>
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -262,7 +262,7 @@ describe('ssr: renderToString', () => {
|
||||
)
|
||||
).toBe(
|
||||
`<div>parent<div class="child">` +
|
||||
`<!----><span>from slot</span><!---->` +
|
||||
`<span>from slot</span>` +
|
||||
`</div></div>`
|
||||
)
|
||||
|
||||
@ -277,7 +277,7 @@ describe('ssr: renderToString', () => {
|
||||
}
|
||||
})
|
||||
)
|
||||
).toBe(`<div>parent<div class="child"><!---->fallback<!----></div></div>`)
|
||||
).toBe(`<div>parent<div class="child">fallback</div></div>`)
|
||||
})
|
||||
|
||||
test('nested components with vnode slots', async () => {
|
||||
@ -321,7 +321,7 @@ describe('ssr: renderToString', () => {
|
||||
)
|
||||
).toBe(
|
||||
`<div>parent<div class="child">` +
|
||||
`<!----><span>from slot</span><!---->` +
|
||||
`<span>from slot</span>` +
|
||||
`</div></div>`
|
||||
)
|
||||
})
|
||||
@ -339,7 +339,7 @@ describe('ssr: renderToString', () => {
|
||||
|
||||
expect(await renderToString(app)).toBe(
|
||||
`<div>parent<div class="child">` +
|
||||
`<!----><span>from slot</span><!---->` +
|
||||
`<span>from slot</span>` +
|
||||
`</div></div>`
|
||||
)
|
||||
})
|
||||
@ -461,9 +461,7 @@ describe('ssr: renderToString', () => {
|
||||
createCommentVNode('qux')
|
||||
])
|
||||
)
|
||||
).toBe(
|
||||
`<div>foo<span>bar</span><!----><span>baz</span><!----><!--qux--></div>`
|
||||
)
|
||||
).toBe(`<div>foo<span>bar</span><span>baz</span><!--qux--></div>`)
|
||||
})
|
||||
|
||||
test('void elements', async () => {
|
||||
|
@ -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(`<!---->`)
|
||||
}
|
||||
|
@ -238,9 +238,7 @@ function renderVNode(
|
||||
push(children ? `<!--${children}-->` : `<!---->`)
|
||||
break
|
||||
case Fragment:
|
||||
push(`<!---->`)
|
||||
renderVNodeChildren(push, children as VNodeArrayChildren, parentComponent)
|
||||
push(`<!---->`)
|
||||
break
|
||||
default:
|
||||
if (shapeFlag & ShapeFlags.ELEMENT) {
|
||||
|
Loading…
Reference in New Issue
Block a user