]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(hmr): force full update on nested child components (#1312)
authorCarlos Rodrigues <david-181@hotmail.com>
Fri, 12 Jun 2020 18:31:56 +0000 (19:31 +0100)
committerGitHub <noreply@github.com>
Fri, 12 Jun 2020 18:31:56 +0000 (14:31 -0400)
packages/runtime-core/__tests__/hmr.spec.ts
packages/runtime-core/src/componentRenderUtils.ts
packages/runtime-core/src/renderer.ts

index e1dab7cf60656a1c5a9f31d046865f00d393f34b..da4bbc39daac1d6ede887a8b974cb2014ed4c2d9 100644 (file)
@@ -218,4 +218,75 @@ describe('hot module replacement', () => {
     rerender(parentId, compileToFunction(`<Child msg="bar" />`))
     expect(serializeInner(root)).toBe(`<div>bar</div>`)
   })
+
+  // #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(`<div>child</div>`)
+    }
+    createRecord(childId, Child)
+
+    const Parent: ComponentOptions = {
+      __hmrId: parentId,
+      components: { Child },
+      render: compileToFunction(`<Child class="test" />`)
+    }
+    createRecord(parentId, Parent)
+
+    render(h(Parent), root)
+    expect(serializeInner(root)).toBe(`<div class="test">child</div>`)
+
+    rerender(parentId, compileToFunction(`<Child/>`))
+    expect(serializeInner(root)).toBe(`<div>child</div>`)
+  })
+
+  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(`<div>child</div>`)
+    }
+    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(`<Child />`)
+        parentComp.components = {
+          Child
+        }
+      } else {
+        parentComp.render = compileToFunction(`<Parent />`)
+        parentComp.components = {
+          Parent: components[i - 1]
+        }
+      }
+
+      createRecord(parentId, parentComp)
+    }
+
+    const last = components[components.length - 1]
+
+    render(h(last), root)
+    expect(serializeInner(root)).toBe(`<div>child</div>`)
+
+    rerender(last.__hmrId!, compileToFunction(`<Parent class="test"/>`))
+    expect(serializeInner(root)).toBe(`<div class="test">child</div>`)
+  })
 })
index 8b4d6ed4268ebca5386a466cca638d9945b04fa2..fb762cbe95507364617c06e4e278466e7539007b 100644 (file)
@@ -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++) {
index d64460ad53ab01a899b2cb9e947a7de030428031..d5c2c8e140f87b3944aacb8f55962d2ee01108c5 100644 (file)
@@ -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) {