From 2408a656627358b21aa49209e64d14a1aeec7825 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 29 May 2020 10:50:01 -0400 Subject: [PATCH] fix(hmr): force full update in child component on slot update --- packages/runtime-core/__tests__/hmr.spec.ts | 14 +++++++------- packages/runtime-core/src/component.ts | 2 +- packages/runtime-core/src/componentRenderUtils.ts | 2 +- packages/runtime-core/src/componentSlots.ts | 15 +++++++++++---- packages/runtime-core/src/hmr.ts | 4 ++-- packages/runtime-core/src/renderer.ts | 4 ++-- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index 45d86daa533..e1dab7cf606 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -45,7 +45,7 @@ describe('hot module replacement', () => { const Child: ComponentOptions = { __hmrId: childId, - render: compileToFunction(``) + render: compileToFunction(`
`) } createRecord(childId, Child) @@ -62,13 +62,13 @@ describe('hot module replacement', () => { createRecord(parentId, Parent) render(h(Parent), root) - expect(serializeInner(root)).toBe(`
00
`) + expect(serializeInner(root)).toBe(`
0
0
`) // 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(`
1
1
`) // // Update text while preserving state rerender( @@ -77,7 +77,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( @@ -86,7 +86,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( @@ -97,7 +97,7 @@ describe('hot module replacement', () => { ` ) ) - expect(serializeInner(root)).toBe(`
111!
`) + expect(serializeInner(root)).toBe(`
11
1!
`) // Should force update child slot elements rerender( @@ -108,7 +108,7 @@ describe('hot module replacement', () => { ` ) ) - expect(serializeInner(root)).toBe(`
1
`) + expect(serializeInner(root)).toBe(`
1
`) }) test('reload', async () => { diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 9ae406e3363..89d9916f4a1 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -313,7 +313,7 @@ export interface ComponentInternalInstance { * hmr marker (dev only) * @internal */ - renderUpdated?: boolean + hmrUpdated?: boolean } const emptyAppContext = createAppContext() diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index d63f804f61f..8b4d6ed4268 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -251,7 +251,7 @@ export function shouldUpdateComponent( __DEV__ && (prevChildren || nextChildren) && parentComponent && - parentComponent.renderUpdated + parentComponent.hmrUpdated ) { return true } diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index 6ed3ef0f09a..b26b6f17a25 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -18,6 +18,7 @@ import { import { warn } from './warning' import { isKeepAlive } from './components/KeepAlive' import { withCtx } from './helpers/withRenderContext' +import { queuePostFlushCb } from './scheduler' export type Slot = (...args: any[]) => VNode[] @@ -124,11 +125,17 @@ export const updateSlots = ( if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) { if ((children as RawSlots)._ === 1) { // compiled slots. - if ( + if (__DEV__ && instance.parent && instance.parent.hmrUpdated) { + // Parent was HMR updated so slot content may have changed. + // force update slots and mark instance for hmr as well + extend(slots, children as Slots) + instance.hmrUpdated = true + queuePostFlushCb(() => { + instance.hmrUpdated = false + }) + } else if ( // bail on dynamic slots (v-if, v-for, reference of scope variables) - !(vnode.patchFlag & PatchFlags.DYNAMIC_SLOTS) && - // bail on HRM updates - !(__DEV__ && instance.parent && instance.parent.renderUpdated) + !(vnode.patchFlag & PatchFlags.DYNAMIC_SLOTS) ) { // compiled AND static. // no need to update, and skip stale slots removal. diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index 0e2dfe750bd..27e5a1b6948 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -70,9 +70,9 @@ function rerender(id: string, newRender?: Function) { } instance.renderCache = [] // this flag forces child components with slot content to update - instance.renderUpdated = true + instance.hmrUpdated = true instance.update() - instance.renderUpdated = false + instance.hmrUpdated = false }) } diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 382cd379eed..e289fbcb9ff 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -779,7 +779,7 @@ function baseCreateRenderer( invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate') } - if (__DEV__ && parentComponent && parentComponent.renderUpdated) { + if (__DEV__ && parentComponent && parentComponent.hmrUpdated) { // HMR updated, force full diff patchFlag = 0 optimized = false @@ -1006,7 +1006,7 @@ function baseCreateRenderer( optimized = true } - if (__DEV__ && parentComponent && parentComponent.renderUpdated) { + if (__DEV__ && parentComponent && parentComponent.hmrUpdated) { // HMR updated, force full diff patchFlag = 0 optimized = false