From d187f39b767a6354e84608b9db17fa8a8b0cdf5d Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Sun, 7 Jun 2020 09:17:38 +0100 Subject: [PATCH 1/5] fix(HMR): class not removed on HMR --- packages/runtime-core/__tests__/hmr.spec.ts | 29 +++++++++++++++++++++ packages/runtime-core/src/renderer.ts | 7 ++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index e1dab7cf606..9a8cfc666af 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -218,4 +218,33 @@ describe('hot module replacement', () => { rerender(parentId, compileToFunction(``)) expect(serializeInner(root)).toBe(`
bar
`) }) + + // #1305 - component should remove class + test('remove class', () => { + const root = nodeOps.createElement('div') + const parentId = 'test-force-props-parent' + const childId = 'test-force-props-child' + + const Child: ComponentOptions = { + __hmrId: childId, + props: { + msg: String + }, + render: compileToFunction(`
{{ msg }}
`) + } + createRecord(childId, Child) + + const Parent: ComponentOptions = { + __hmrId: parentId, + components: { Child }, + render: compileToFunction(``) + } + createRecord(parentId, Parent) + + render(h(Parent), root) + expect(serializeInner(root)).toBe(`
foo
`) + + rerender(parentId, compileToFunction(``)) + expect(serializeInner(root)).toBe(`
bar
`) + }) }) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index e289fbcb9ff..45b84cb964c 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -779,7 +779,12 @@ function baseCreateRenderer( invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate') } - if (__DEV__ && parentComponent && parentComponent.hmrUpdated) { + if ( + __DEV__ && + parentComponent && + (parentComponent.hmrUpdated || + (parentComponent.parent && parentComponent.parent.hmrUpdated)) + ) { // HMR updated, force full diff patchFlag = 0 optimized = false From 6c3180d23024ff4e3753ea273c372da7fd3b6968 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Sun, 7 Jun 2020 09:25:12 +0100 Subject: [PATCH 2/5] chore: reduce test changes --- packages/runtime-core/__tests__/hmr.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index 9a8cfc666af..ed38457dce1 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -244,7 +244,7 @@ describe('hot module replacement', () => { render(h(Parent), root) expect(serializeInner(root)).toBe(`
foo
`) - rerender(parentId, compileToFunction(``)) - expect(serializeInner(root)).toBe(`
bar
`) + rerender(parentId, compileToFunction(``)) + expect(serializeInner(root)).toBe(`
foo
`) }) }) From 2ca00aaac94e67bea5b534094c62c1860fbbf2d6 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Sun, 7 Jun 2020 09:25:34 +0100 Subject: [PATCH 3/5] chore: rename test --- packages/runtime-core/__tests__/hmr.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index ed38457dce1..97e599690e1 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -220,7 +220,7 @@ describe('hot module replacement', () => { }) // #1305 - component should remove class - test('remove class', () => { + test('remove static class from parent', () => { const root = nodeOps.createElement('div') const parentId = 'test-force-props-parent' const childId = 'test-force-props-child' From ab233e983f6d5c003056737a122fc70f710c4220 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Sun, 7 Jun 2020 09:38:17 +0100 Subject: [PATCH 4/5] chore: update test --- packages/runtime-core/__tests__/hmr.spec.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index 97e599690e1..6d52e9a69df 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -227,24 +227,21 @@ describe('hot module replacement', () => { const Child: ComponentOptions = { __hmrId: childId, - props: { - msg: String - }, - render: compileToFunction(`
{{ msg }}
`) + render: compileToFunction(`
child
`) } createRecord(childId, Child) const Parent: ComponentOptions = { __hmrId: parentId, components: { Child }, - render: compileToFunction(``) + render: compileToFunction(``) } createRecord(parentId, Parent) render(h(Parent), root) - expect(serializeInner(root)).toBe(`
foo
`) + expect(serializeInner(root)).toBe(`
child
`) - rerender(parentId, compileToFunction(``)) - expect(serializeInner(root)).toBe(`
foo
`) + rerender(parentId, compileToFunction(``)) + expect(serializeInner(root)).toBe(`
child
`) }) }) From 3223d033f25b9c04c51e41b08c994d712007a3d9 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Fri, 12 Jun 2020 19:12:21 +0100 Subject: [PATCH 5/5] chore: allow any parent in the parent chaing be updated --- packages/runtime-core/__tests__/hmr.spec.ts | 49 ++++++++++++++++++- .../runtime-core/src/componentRenderUtils.ts | 19 ++++--- packages/runtime-core/src/renderer.ts | 22 +++++---- 3 files changed, 70 insertions(+), 20 deletions(-) diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index 6d52e9a69df..da4bbc39daa 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -222,8 +222,8 @@ describe('hot module replacement', () => { // #1305 - component should remove class test('remove static class from parent', () => { const root = nodeOps.createElement('div') - const parentId = 'test-force-props-parent' - const childId = 'test-force-props-child' + const parentId = 'test-force-class-parent' + const childId = 'test-force-class-child' const Child: ComponentOptions = { __hmrId: childId, @@ -244,4 +244,49 @@ describe('hot module replacement', () => { rerender(parentId, compileToFunction(``)) expect(serializeInner(root)).toBe(`
child
`) }) + + test('rerender if any parent in the parent chain', () => { + const root = nodeOps.createElement('div') + const parent = 'test-force-props-parent-' + const childId = 'test-force-props-child' + + const numberOfParents = 5 + + const Child: ComponentOptions = { + __hmrId: childId, + render: compileToFunction(`
child
`) + } + createRecord(childId, Child) + + const components: ComponentOptions[] = [] + + for (let i = 0; i < numberOfParents; i++) { + const parentId = `${parent}${i}` + const parentComp: ComponentOptions = { + __hmrId: parentId + } + components.push(parentComp) + if (i === 0) { + parentComp.render = compileToFunction(``) + parentComp.components = { + Child + } + } else { + parentComp.render = compileToFunction(``) + parentComp.components = { + Parent: components[i - 1] + } + } + + createRecord(parentId, parentComp) + } + + const last = components[components.length - 1] + + render(h(last), root) + expect(serializeInner(root)).toBe(`
child
`) + + rerender(last.__hmrId!, compileToFunction(``)) + expect(serializeInner(root)).toBe(`
child
`) + }) }) diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index 8b4d6ed4268..fb762cbe955 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -247,13 +247,13 @@ export function shouldUpdateComponent( // Parent component's render function was hot-updated. Since this may have // caused the child component's slots content to have changed, we need to // force the child to update as well. - if ( - __DEV__ && - (prevChildren || nextChildren) && - parentComponent && - parentComponent.hmrUpdated - ) { - return true + if (__DEV__ && (prevChildren || nextChildren) && parentComponent) { + let parent: ComponentInternalInstance | null = parentComponent + do { + if (parent.hmrUpdated) { + return true + } + } while ((parent = parent.parent)) } // force child update for runtime directive or transition on component vnode. @@ -268,8 +268,11 @@ export function shouldUpdateComponent( return true } if (patchFlag & PatchFlags.FULL_PROPS) { + if (!prevProps) { + return !!nextProps + } // presence of this flag indicates props are always non-null - return hasPropsChanged(prevProps!, nextProps!) + return hasPropsChanged(prevProps, nextProps!) } else if (patchFlag & PatchFlags.PROPS) { const dynamicProps = nextVNode.dynamicProps! for (let i = 0; i < dynamicProps.length; i++) { diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 45b84cb964c..2bbb5a58153 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -779,16 +779,18 @@ function baseCreateRenderer( invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate') } - if ( - __DEV__ && - parentComponent && - (parentComponent.hmrUpdated || - (parentComponent.parent && parentComponent.parent.hmrUpdated)) - ) { - // HMR updated, force full diff - patchFlag = 0 - optimized = false - dynamicChildren = null + // check if any component of the parent chain has `hmrUpdated` + if (__DEV__ && parentComponent) { + let parent: ComponentInternalInstance | null = parentComponent + do { + if (parent.hmrUpdated) { + // HMR updated, force full diff + patchFlag = 0 + optimized = false + dynamicChildren = null + break + } + } while ((parent = parent.parent)) } if (patchFlag > 0) {