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) {