fix(compiler-core): handle template root and template v-if as stable fragments
This commit is contained in:
parent
f77ae132e5
commit
8ffd79c754
@ -5,6 +5,7 @@ module.exports = {
|
||||
__TEST__: true,
|
||||
__VERSION__: require('./package.json').version,
|
||||
__BROWSER__: false,
|
||||
__BUNDLER__: false,
|
||||
__RUNTIME_COMPILE__: true,
|
||||
__FEATURE_OPTIONS__: true,
|
||||
__FEATURE_SUSPENSE__: true
|
||||
|
@ -19,7 +19,7 @@ return function render() {
|
||||
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||
_createVNode(\\"span\\", null, _toString(value + index), 1 /* TEXT */)
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
], 2 /* CLASS */))
|
||||
}
|
||||
}"
|
||||
@ -42,7 +42,7 @@ return function render() {
|
||||
return (openBlock(), createBlock(\\"div\\", null, [
|
||||
createVNode(\\"span\\", null, toString(value + index), 1 /* TEXT */)
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
], 2 /* CLASS */))
|
||||
}"
|
||||
`;
|
||||
@ -64,7 +64,7 @@ export default function render() {
|
||||
return (openBlock(), createBlock(\\"div\\", null, [
|
||||
createVNode(\\"span\\", null, toString(value + index), 1 /* TEXT */)
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
], 2 /* CLASS */))
|
||||
}"
|
||||
`;
|
||||
|
@ -21,6 +21,8 @@ import { transformFor } from '../src/transforms/vFor'
|
||||
import { transformElement } from '../src/transforms/transformElement'
|
||||
import { transformSlotOutlet } from '../src/transforms/transformSlotOutlet'
|
||||
import { transformText } from '../src/transforms/transformText'
|
||||
import { genFlagText } from './testUtils'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
|
||||
describe('compiler: transform', () => {
|
||||
test('context state', () => {
|
||||
@ -352,7 +354,8 @@ describe('compiler: transform', () => {
|
||||
[
|
||||
{ type: NodeTypes.ELEMENT, tag: `div` },
|
||||
{ type: NodeTypes.ELEMENT, tag: `div` }
|
||||
]
|
||||
],
|
||||
genFlagText(PatchFlags.STABLE_FRAGMENT)
|
||||
])
|
||||
)
|
||||
})
|
||||
|
@ -235,7 +235,7 @@ return function render() {
|
||||
return (_openBlock(), _createBlock(\\"p\\", null, [
|
||||
_createVNode(\\"span\\", null, _toString(o + 'foo'), 1 /* TEXT */)
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
]))
|
||||
}
|
||||
}"
|
||||
@ -270,7 +270,7 @@ return function render() {
|
||||
return (_openBlock(), _createBlock(\\"p\\", null, [
|
||||
_createVNode(\\"span\\", null, _toString(o), 1 /* TEXT */)
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
]))
|
||||
}
|
||||
}"
|
||||
@ -362,7 +362,7 @@ return function render() {
|
||||
return (_openBlock(), _createBlock(\\"div\\", _hoisted_1, [
|
||||
_hoisted_2
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
]))
|
||||
}
|
||||
}"
|
||||
|
@ -23,7 +23,7 @@ return function render() {
|
||||
_createVNode(\\"div\\"),
|
||||
_createTextVNode(_toString(foo) + \\" bar \\" + _toString(baz), 1 /* TEXT */),
|
||||
_createVNode(\\"div\\")
|
||||
]))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -41,7 +41,7 @@ return function render() {
|
||||
_createVNode(\\"div\\"),
|
||||
_createTextVNode(\\"hello\\"),
|
||||
_createVNode(\\"div\\")
|
||||
]))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -69,7 +69,7 @@ return function render() {
|
||||
_createVNode(\\"div\\"),
|
||||
_createTextVNode(\\"hello\\"),
|
||||
_createVNode(\\"div\\")
|
||||
]))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
@ -9,7 +9,7 @@ return function render() {
|
||||
|
||||
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
|
||||
return (_openBlock(), _createBlock(\\"span\\"))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -25,8 +25,8 @@ return function render() {
|
||||
return (_openBlock(), _createBlock(_Fragment, { key: item }, [
|
||||
\\"hello\\",
|
||||
_createVNode(\\"span\\")
|
||||
]))
|
||||
}), 64 /* KEYED_FRAGMENT */))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}), 128 /* KEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -40,7 +40,7 @@ return function render() {
|
||||
|
||||
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
|
||||
return (_openBlock(), _createBlock(\\"span\\", { key: item }))
|
||||
}), 64 /* KEYED_FRAGMENT */))
|
||||
}), 128 /* KEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -54,7 +54,7 @@ return function render() {
|
||||
|
||||
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item, __, index) => {
|
||||
return (_openBlock(), _createBlock(\\"span\\"))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -68,7 +68,7 @@ return function render() {
|
||||
|
||||
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (_, __, index) => {
|
||||
return (_openBlock(), _createBlock(\\"span\\"))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -82,7 +82,7 @@ return function render() {
|
||||
|
||||
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (_, key, index) => {
|
||||
return (_openBlock(), _createBlock(\\"span\\"))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -98,8 +98,8 @@ return function render() {
|
||||
return (_openBlock(), _createBlock(_Fragment, null, [
|
||||
\\"hello\\",
|
||||
_createVNode(\\"span\\")
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -113,7 +113,7 @@ return function render() {
|
||||
|
||||
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
|
||||
return _renderSlot($slots, \\"default\\")
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -127,7 +127,7 @@ return function render() {
|
||||
|
||||
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item) => {
|
||||
return _renderSlot($slots, \\"default\\")
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -145,7 +145,7 @@ return function render() {
|
||||
return (_openBlock(), _withDirectives(_createBlock(\\"div\\", null, null, 32 /* NEED_PATCH */), [
|
||||
[_directive_foo]
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -160,7 +160,7 @@ return function render() {
|
||||
return (_openBlock(), ok
|
||||
? _createBlock(_Fragment, { key: 0 }, _renderList(list, (i) => {
|
||||
return (_openBlock(), _createBlock(\\"div\\"))
|
||||
}), 128 /* UNKEYED_FRAGMENT */)
|
||||
}), 256 /* UNKEYED_FRAGMENT */)
|
||||
: _createCommentVNode(\\"v-if\\", true))
|
||||
}
|
||||
}"
|
||||
@ -175,7 +175,7 @@ return function render() {
|
||||
|
||||
return (_openBlock(false), _createBlock(_Fragment, null, _renderList(items, (item, key, index) => {
|
||||
return (_openBlock(), _createBlock(\\"span\\"))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
}), 256 /* UNKEYED_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
@ -11,7 +11,7 @@ return function render() {
|
||||
[_ctx.one]: ({ foo }) => [toString(foo), toString(_ctx.bar)],
|
||||
[_ctx.two]: ({ bar }) => [toString(_ctx.foo), toString(bar)],
|
||||
_compiled: true
|
||||
}, 256 /* DYNAMIC_SLOTS */))
|
||||
}, 512 /* DYNAMIC_SLOTS */))
|
||||
}"
|
||||
`;
|
||||
|
||||
@ -59,7 +59,7 @@ return function render() {
|
||||
fn: () => [toString(name)]
|
||||
}
|
||||
})
|
||||
]), 256 /* DYNAMIC_SLOTS */))
|
||||
]), 512 /* DYNAMIC_SLOTS */))
|
||||
}"
|
||||
`;
|
||||
|
||||
@ -77,7 +77,7 @@ return function render() {
|
||||
fn: (props) => [toString(props)]
|
||||
}
|
||||
: undefined
|
||||
]), 256 /* DYNAMIC_SLOTS */))
|
||||
]), 512 /* DYNAMIC_SLOTS */))
|
||||
}"
|
||||
`;
|
||||
|
||||
@ -105,7 +105,7 @@ return function render() {
|
||||
name: \\"one\\",
|
||||
fn: () => [\\"baz\\"]
|
||||
}
|
||||
]), 256 /* DYNAMIC_SLOTS */))
|
||||
]), 512 /* DYNAMIC_SLOTS */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -126,7 +126,7 @@ return function render() {
|
||||
fn: () => [\\"hello\\"]
|
||||
}
|
||||
: undefined
|
||||
]), 256 /* DYNAMIC_SLOTS */))
|
||||
]), 512 /* DYNAMIC_SLOTS */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@ -159,7 +159,7 @@ return function render() {
|
||||
createVNode(_component_Inner, null, {
|
||||
default: ({ bar }) => [toString(foo), toString(bar), toString(_ctx.baz)],
|
||||
_compiled: true
|
||||
}, 256 /* DYNAMIC_SLOTS */),
|
||||
}, 512 /* DYNAMIC_SLOTS */),
|
||||
\\" \\",
|
||||
toString(foo),
|
||||
toString(_ctx.bar),
|
||||
|
@ -25,7 +25,7 @@ import {
|
||||
RENDER_SLOT,
|
||||
WITH_DIRECTIVES
|
||||
} from '../../src/runtimeHelpers'
|
||||
import { PatchFlags } from '@vue/runtime-dom'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
import { createObjectMatcher, genFlagText } from '../testUtils'
|
||||
|
||||
function parseWithForTransform(
|
||||
@ -704,7 +704,8 @@ describe('compiler: v-for', () => {
|
||||
[
|
||||
{ type: NodeTypes.TEXT, content: `hello` },
|
||||
{ type: NodeTypes.ELEMENT, tag: `span` }
|
||||
]
|
||||
],
|
||||
genFlagText(PatchFlags.STABLE_FRAGMENT)
|
||||
]
|
||||
})
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
@ -784,7 +785,8 @@ describe('compiler: v-for', () => {
|
||||
[
|
||||
{ type: NodeTypes.TEXT, content: `hello` },
|
||||
{ type: NodeTypes.ELEMENT, tag: `span` }
|
||||
]
|
||||
],
|
||||
genFlagText(PatchFlags.STABLE_FRAGMENT)
|
||||
]
|
||||
})
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
|
@ -290,8 +290,8 @@ function finalizeRoot(root: RootNode, context: TransformContext) {
|
||||
helper(FRAGMENT),
|
||||
`null`,
|
||||
root.children,
|
||||
`${PatchFlags.UNKEYED_FRAGMENT} /* ${
|
||||
PatchFlagNames[PatchFlags.UNKEYED_FRAGMENT]
|
||||
`${PatchFlags.STABLE_FRAGMENT} /* ${
|
||||
PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
|
||||
} */`
|
||||
]),
|
||||
context
|
||||
|
@ -146,7 +146,10 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
createCallExpression(helper(CREATE_BLOCK), [
|
||||
helper(FRAGMENT),
|
||||
keyProperty ? createObjectExpression([keyProperty]) : `null`,
|
||||
node.children
|
||||
node.children,
|
||||
`${PatchFlags.STABLE_FRAGMENT} /* ${
|
||||
PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
|
||||
} */`
|
||||
]),
|
||||
context
|
||||
)
|
||||
|
@ -16,7 +16,7 @@ return function render() {
|
||||
_createVNode(\\"div\\", null, \\"test\\"),
|
||||
_createVNode(\\"div\\", { style: _hoisted_1 }, \\"red\\"),
|
||||
_createVNode(\\"div\\", { style: {color: 'green'} }, null, 4 /* STYLE */)
|
||||
]))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
@ -34,6 +34,6 @@ export default function render() {
|
||||
createVNode(\\"img\\", { src: _imports_0 }),
|
||||
createVNode(\\"img\\", { src: _imports_1 }),
|
||||
createVNode(\\"img\\", { src: _imports_1 })
|
||||
]))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}"
|
||||
`;
|
||||
|
@ -44,6 +44,6 @@ export default function render() {
|
||||
src: \\"./logo.png\\",
|
||||
srcset: _hoisted_7
|
||||
})
|
||||
]))
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}"
|
||||
`;
|
||||
|
@ -10,7 +10,8 @@ import {
|
||||
resetOps,
|
||||
dumpOps,
|
||||
NodeOpTypes,
|
||||
serializeInner
|
||||
serializeInner,
|
||||
createTextVNode
|
||||
} from '@vue/runtime-test'
|
||||
|
||||
describe('renderer: fragment', () => {
|
||||
@ -110,7 +111,10 @@ describe('renderer: fragment', () => {
|
||||
createVNode(
|
||||
Fragment,
|
||||
null,
|
||||
[h('div', 'one'), 'two'],
|
||||
[
|
||||
createVNode('div', null, 'one', PatchFlags.TEXT),
|
||||
createTextVNode('two')
|
||||
],
|
||||
PatchFlags.UNKEYED_FRAGMENT
|
||||
),
|
||||
root
|
||||
@ -121,7 +125,11 @@ describe('renderer: fragment', () => {
|
||||
createVNode(
|
||||
Fragment,
|
||||
null,
|
||||
[h('div', 'foo'), 'bar', 'baz'],
|
||||
[
|
||||
createVNode('div', null, 'foo', PatchFlags.TEXT),
|
||||
createTextVNode('bar'),
|
||||
createTextVNode('baz')
|
||||
],
|
||||
PatchFlags.KEYED_FRAGMENT
|
||||
),
|
||||
root
|
||||
|
@ -44,6 +44,7 @@ export const PatchFlags = PublicPatchFlags as {
|
||||
PROPS: number
|
||||
NEED_PATCH: number
|
||||
FULL_PROPS: number
|
||||
STABLE_FRAGMENT: number
|
||||
KEYED_FRAGMENT: number
|
||||
UNKEYED_FRAGMENT: number
|
||||
DYNAMIC_SLOTS: number
|
||||
|
@ -654,10 +654,14 @@ export function createRenderer<
|
||||
const fragmentEndAnchor = (n2.anchor = n1
|
||||
? n1.anchor
|
||||
: hostCreateComment(showID ? `fragment-${devFragmentID}-end` : ''))!
|
||||
const { patchFlag } = n2
|
||||
if (patchFlag > 0) {
|
||||
optimized = true
|
||||
}
|
||||
if (n1 == null) {
|
||||
if (showID) {
|
||||
devFragmentID++
|
||||
}
|
||||
if (n1 == null) {
|
||||
hostInsert(fragmentStartAnchor, container, anchor)
|
||||
hostInsert(fragmentEndAnchor, container, anchor)
|
||||
// a fragment can only have array children
|
||||
@ -673,6 +677,22 @@ export function createRenderer<
|
||||
optimized
|
||||
)
|
||||
} else {
|
||||
if (patchFlag & PatchFlags.STABLE_FRAGMENT && n2.dynamicChildren) {
|
||||
// a stable fragment (template root or <template v-for>) doesn't need to
|
||||
// patch children order, but it may contain dynamicChildren.
|
||||
patchBlockChildren(
|
||||
n1.dynamicChildren!,
|
||||
n2.dynamicChildren,
|
||||
container,
|
||||
parentComponent,
|
||||
parentSuspense,
|
||||
isSVG
|
||||
)
|
||||
} else {
|
||||
// keyed / unkeyed, or manual fragments.
|
||||
// for keyed & unkeyed, since they are compiler generated from v-for,
|
||||
// each child is guarunteed to be a block so the fragment will never
|
||||
// have dynamicChildren.
|
||||
patchChildren(
|
||||
n1,
|
||||
n2,
|
||||
@ -685,6 +705,7 @@ export function createRenderer<
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processPortal(
|
||||
n1: HostVNode | null,
|
||||
@ -1033,7 +1054,6 @@ export function createRenderer<
|
||||
}
|
||||
// fast path
|
||||
if (patchFlag > 0) {
|
||||
optimized = true
|
||||
if (patchFlag & PatchFlags.KEYED_FRAGMENT) {
|
||||
// this could be either fully-keyed or mixed (some keyed some not)
|
||||
// presence of patchFlag means children are guaranteed to be arrays
|
||||
@ -1215,7 +1235,7 @@ export function createRenderer<
|
||||
while (i <= e1 && i <= e2) {
|
||||
const n1 = c1[e1]
|
||||
const n2 = optimized
|
||||
? (c2[i] as HostVNode)
|
||||
? (c2[e2] as HostVNode)
|
||||
: (c2[e2] = normalizeVNode(c2[e2]))
|
||||
if (isSameVNodeType(n1, n2)) {
|
||||
patch(
|
||||
|
@ -47,16 +47,19 @@ export const enum PatchFlags {
|
||||
// value.
|
||||
NEED_PATCH = 1 << 5,
|
||||
|
||||
// Indicates a fragment whose children order doesn't change.
|
||||
STABLE_FRAGMENT = 1 << 6,
|
||||
|
||||
// Indicates a fragment with keyed or partially keyed children
|
||||
KEYED_FRAGMENT = 1 << 6,
|
||||
KEYED_FRAGMENT = 1 << 7,
|
||||
|
||||
// Indicates a fragment with unkeyed children.
|
||||
UNKEYED_FRAGMENT = 1 << 7,
|
||||
UNKEYED_FRAGMENT = 1 << 8,
|
||||
|
||||
// Indicates a component with dynamic slots (e.g. slot that references a v-for
|
||||
// iterated value, or dynamic slot names).
|
||||
// Components with this flag are always force updated.
|
||||
DYNAMIC_SLOTS = 1 << 8,
|
||||
DYNAMIC_SLOTS = 1 << 9,
|
||||
|
||||
// A special flag that indicates that the diffing algorithm should bail out
|
||||
// of optimized mode. This is only on block fragments created by renderSlot()
|
||||
@ -87,6 +90,7 @@ export const PatchFlagNames = {
|
||||
[PatchFlags.PROPS]: `PROPS`,
|
||||
[PatchFlags.NEED_PATCH]: `NEED_PATCH`,
|
||||
[PatchFlags.FULL_PROPS]: `FULL_PROPS`,
|
||||
[PatchFlags.STABLE_FRAGMENT]: `STABLE_FRAGMENT`,
|
||||
[PatchFlags.KEYED_FRAGMENT]: `KEYED_FRAGMENT`,
|
||||
[PatchFlags.UNKEYED_FRAGMENT]: `UNKEYED_FRAGMENT`,
|
||||
[PatchFlags.DYNAMIC_SLOTS]: `DYNAMIC_SLOTS`,
|
||||
|
@ -14,7 +14,11 @@ export function setupPuppeteer() {
|
||||
|
||||
page.on('console', e => {
|
||||
if (e.type() === 'error') {
|
||||
console.error(`Error from Puppeteer-loaded page:`, e)
|
||||
const err = e.args()[0] as any
|
||||
console.error(
|
||||
`Error from Puppeteer-loaded page:\n`,
|
||||
err._remoteObject.description
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user