From 8ffd79c75402d73280dc3bc948599a8d7416676e Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 12 Dec 2019 20:46:20 -0500 Subject: [PATCH] fix(compiler-core): handle template root and template v-if as stable fragments --- jest.config.js | 1 + .../__snapshots__/compile.spec.ts.snap | 6 +-- .../compiler-core/__tests__/transform.spec.ts | 5 +- .../__snapshots__/hoistStatic.spec.ts.snap | 6 +-- .../__snapshots__/transformText.spec.ts.snap | 6 +-- .../__snapshots__/vFor.spec.ts.snap | 28 +++++------ .../__snapshots__/vSlot.spec.ts.snap | 12 ++--- .../__tests__/transforms/vFor.spec.ts | 8 ++-- packages/compiler-core/src/transform.ts | 4 +- packages/compiler-core/src/transforms/vFor.ts | 5 +- .../__snapshots__/index.spec.ts.snap | 2 +- .../templateTransformAssetUrl.spec.ts.snap | 2 +- .../templateTransformSrcset.spec.ts.snap | 2 +- .../__tests__/rendererFragment.spec.ts | 14 ++++-- packages/runtime-core/src/index.ts | 1 + packages/runtime-core/src/renderer.ts | 48 +++++++++++++------ packages/shared/src/patchFlags.ts | 10 ++-- packages/vue/examples/__tests__/e2eUtils.ts | 6 ++- 18 files changed, 106 insertions(+), 60 deletions(-) diff --git a/jest.config.js b/jest.config.js index 2bec8c3137..5d9067c8ec 100644 --- a/jest.config.js +++ b/jest.config.js @@ -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 diff --git a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap index 3f7f1c5959..e3e4c726c0 100644 --- a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap @@ -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 */)) }" `; diff --git a/packages/compiler-core/__tests__/transform.spec.ts b/packages/compiler-core/__tests__/transform.spec.ts index baaddd788f..fb67a5d1ff 100644 --- a/packages/compiler-core/__tests__/transform.spec.ts +++ b/packages/compiler-core/__tests__/transform.spec.ts @@ -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) ]) ) }) 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 8d83391ba2..5173080d28 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap @@ -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 */)) ])) } }" 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 e0327d8a47..a40a6f44dc 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/transformText.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/transformText.spec.ts.snap @@ -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 */)) } }" `; diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap index 372b077124..edf9326f63 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -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 */)) } }" `; diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap index dd819d03a0..c7796e246e 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -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), diff --git a/packages/compiler-core/__tests__/transforms/vFor.spec.ts b/packages/compiler-core/__tests__/transforms/vFor.spec.ts index 39b34c532d..93b6bc9031 100644 --- a/packages/compiler-core/__tests__/transforms/vFor.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vFor.spec.ts @@ -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() diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index dfa73edc3e..0e32d29884 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -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 diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index 418999cbc1..a31a47e8ed 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -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 ) diff --git a/packages/compiler-dom/__tests__/__snapshots__/index.spec.ts.snap b/packages/compiler-dom/__tests__/__snapshots__/index.spec.ts.snap index 1b8d51ebb2..7c9d04a234 100644 --- a/packages/compiler-dom/__tests__/__snapshots__/index.spec.ts.snap +++ b/packages/compiler-dom/__tests__/__snapshots__/index.spec.ts.snap @@ -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 */)) } }" `; diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap index 4004ddbcf0..580a70b4f7 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformAssetUrl.spec.ts.snap @@ -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 */)) }" `; diff --git a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap index 444b0ea4d3..2ea6edbd60 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/templateTransformSrcset.spec.ts.snap @@ -44,6 +44,6 @@ export default function render() { src: \\"./logo.png\\", srcset: _hoisted_7 }) - ])) + ], 64 /* STABLE_FRAGMENT */)) }" `; diff --git a/packages/runtime-core/__tests__/rendererFragment.spec.ts b/packages/runtime-core/__tests__/rendererFragment.spec.ts index 8c6aef0167..7c4be2b3f5 100644 --- a/packages/runtime-core/__tests__/rendererFragment.spec.ts +++ b/packages/runtime-core/__tests__/rendererFragment.spec.ts @@ -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 diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index de44fdd013..2629f0f379 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -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 diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 3a27390e10..b3dbc52d09 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -654,10 +654,14 @@ export function createRenderer< const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateComment(showID ? `fragment-${devFragmentID}-end` : ''))! - if (showID) { - devFragmentID++ + const { patchFlag } = n2 + if (patchFlag > 0) { + optimized = true } if (n1 == null) { + if (showID) { + devFragmentID++ + } hostInsert(fragmentStartAnchor, container, anchor) hostInsert(fragmentEndAnchor, container, anchor) // a fragment can only have array children @@ -673,16 +677,33 @@ export function createRenderer< optimized ) } else { - patchChildren( - n1, - n2, - container, - fragmentEndAnchor, - parentComponent, - parentSuspense, - isSVG, - optimized - ) + if (patchFlag & PatchFlags.STABLE_FRAGMENT && n2.dynamicChildren) { + // a stable fragment (template root or