diff --git a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap index c552bfe8dc8..223e7538a80 100644 --- a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap @@ -5,8 +5,8 @@ exports[`scopeId compiler support should push scopeId for hoisted nodes 1`] = ` const _withId = _withScopeId(\\"test\\") _pushScopeId(\\"test\\") -const _hoisted_1 = _createVNode(\\"div\\", null, \\"hello\\") -const _hoisted_2 = _createVNode(\\"div\\", null, \\"world\\") +const _hoisted_1 = _createVNode(\\"div\\", null, \\"hello\\", -1) +const _hoisted_2 = _createVNode(\\"div\\", null, \\"world\\", -1) _popScopeId() export const render = _withId(function render(_ctx, _cache) { diff --git a/packages/compiler-core/__tests__/scopeId.spec.ts b/packages/compiler-core/__tests__/scopeId.spec.ts index f009b87a0b1..000b93739c1 100644 --- a/packages/compiler-core/__tests__/scopeId.spec.ts +++ b/packages/compiler-core/__tests__/scopeId.spec.ts @@ -81,8 +81,8 @@ describe('scopeId compiler support', () => { expect(code).toMatch( [ `_pushScopeId("test")`, - `const _hoisted_1 = _createVNode("div", null, "hello")`, - `const _hoisted_2 = _createVNode("div", null, "world")`, + `const _hoisted_1 = _createVNode("div", null, "hello", -1)`, + `const _hoisted_2 = _createVNode("div", null, "world", -1)`, `_popScopeId()` ].join('\n') ) 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 f4f1936b594..a6604643684 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap @@ -4,7 +4,7 @@ exports[`compiler: hoistStatic transform hoist element with static key 1`] = ` "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = _createVNode(\\"div\\", { key: \\"foo\\" }) +const _hoisted_1 = _createVNode(\\"div\\", { key: \\"foo\\" }, null, -1) return function render(_ctx, _cache) { with (this) { @@ -24,7 +24,7 @@ const { createVNode: _createVNode } = _Vue const _hoisted_1 = _createVNode(\\"p\\", null, [ _createVNode(\\"span\\"), _createVNode(\\"span\\") -]) +], -1) return function render(_ctx, _cache) { with (this) { @@ -43,7 +43,7 @@ const { createVNode: _createVNode, createCommentVNode: _createCommentVNode } = _ const _hoisted_1 = _createVNode(\\"div\\", null, [ _createCommentVNode(\\"comment\\") -]) +], -1) return function render(_ctx, _cache) { with (this) { @@ -60,8 +60,8 @@ exports[`compiler: hoistStatic transform hoist siblings with common non-hoistabl "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = _createVNode(\\"span\\") -const _hoisted_2 = _createVNode(\\"div\\") +const _hoisted_1 = _createVNode(\\"span\\", null, null, -1) +const _hoisted_2 = _createVNode(\\"div\\", null, null, -1) return function render(_ctx, _cache) { with (this) { @@ -79,7 +79,7 @@ exports[`compiler: hoistStatic transform hoist simple element 1`] = ` "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = _createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\") +const _hoisted_1 = _createVNode(\\"span\\", { class: \\"inline\\" }, \\"hello\\", -1) return function render(_ctx, _cache) { with (this) { @@ -172,7 +172,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = _createVNode(\\"span\\", null, \\"foo \\" + _toDisplayString(1) + \\" \\" + _toDisplayString(true)) +const _hoisted_1 = _createVNode(\\"span\\", null, \\"foo \\" + _toDisplayString(1) + \\" \\" + _toDisplayString(true), -1) return function render(_ctx, _cache) { with (this) { @@ -189,7 +189,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t "const _Vue = Vue const { createVNode: _createVNode } = _Vue -const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toDisplayString(1)) +const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toDisplayString(1), -1) return function render(_ctx, _cache) { with (this) { @@ -346,7 +346,7 @@ exports[`compiler: hoistStatic transform should hoist v-for children if static 1 const { createVNode: _createVNode } = _Vue const _hoisted_1 = { id: \\"foo\\" } -const _hoisted_2 = _createVNode(\\"span\\") +const _hoisted_2 = _createVNode(\\"span\\", null, null, -1) return function render(_ctx, _cache) { with (this) { @@ -371,7 +371,7 @@ const _hoisted_1 = { key: 0, id: \\"foo\\" } -const _hoisted_2 = _createVNode(\\"span\\") +const _hoisted_2 = _createVNode(\\"span\\", null, null, -1) return function render(_ctx, _cache) { with (this) { diff --git a/packages/compiler-core/src/transforms/hoistStatic.ts b/packages/compiler-core/src/transforms/hoistStatic.ts index 2bd0b608e4d..06d9eaf74bd 100644 --- a/packages/compiler-core/src/transforms/hoistStatic.ts +++ b/packages/compiler-core/src/transforms/hoistStatic.ts @@ -52,6 +52,7 @@ function walk( ) { if (!doNotHoistNode && isStaticNode(child, resultCache)) { // whole tree is static + ;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.HOISTED + `` const hoisted = context.transformHoist ? context.transformHoist(child, context) : child.codegenNode! diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 888451f5c60..01b66c4895c 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -411,8 +411,12 @@ export function createRenderer< optimized: boolean ) { let el: HostElement - const { type, props, shapeFlag, transition, scopeId } = vnode - if (vnode.el != null && hostCloneNode !== undefined) { + const { type, props, shapeFlag, transition, scopeId, patchFlag } = vnode + if ( + vnode.el !== null && + hostCloneNode !== undefined && + patchFlag === PatchFlags.HOISTED + ) { // If a vnode has non-null el, it means it's being reused. // Only static vnodes can be reused, so its mounted DOM nodes should be // exactly the same, and we can simply do a clone here. diff --git a/packages/shared/src/patchFlags.ts b/packages/shared/src/patchFlags.ts index 20adcefb467..86dff111931 100644 --- a/packages/shared/src/patchFlags.ts +++ b/packages/shared/src/patchFlags.ts @@ -61,11 +61,14 @@ export const enum PatchFlags { // Components with this flag are always force updated. DYNAMIC_SLOTS = 1 << 9, + // A special flag that indicates a hoisted, static vnode. + HOISTED = -1, + // A special flag that indicates that the diffing algorithm should bail out // of optimized mode. This is only on block fragments created by renderSlot() // when encountering non-compiler generated slots (i.e. manually written // render functions, which should always be fully diffed) - BAIL = -1 + BAIL = -2 } // runtime object for public consumption