diff --git a/packages/runtime-core/__tests__/hmr.spec.ts b/packages/runtime-core/__tests__/hmr.spec.ts index e1dab7cf..da4bbc39 100644 --- a/packages/runtime-core/__tests__/hmr.spec.ts +++ b/packages/runtime-core/__tests__/hmr.spec.ts @@ -218,4 +218,75 @@ describe('hot module replacement', () => { rerender(parentId, compileToFunction(``)) expect(serializeInner(root)).toBe(`
bar
`) }) + + // #1305 - component should remove class + test('remove static class from parent', () => { + const root = nodeOps.createElement('div') + const parentId = 'test-force-class-parent' + const childId = 'test-force-class-child' + + const Child: ComponentOptions = { + __hmrId: childId, + render: compileToFunction(`
child
`) + } + createRecord(childId, Child) + + const Parent: ComponentOptions = { + __hmrId: parentId, + components: { Child }, + render: compileToFunction(``) + } + createRecord(parentId, Parent) + + render(h(Parent), root) + expect(serializeInner(root)).toBe(`
child
`) + + 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 8b4d6ed4..fb762cbe 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 d64460ad..d5c2c8e1 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -791,11 +791,18 @@ function baseCreateRenderer( invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate') } - if (__DEV__ && parentComponent && parentComponent.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) {