]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(hmr): force full update in child component on slot update
authorEvan You <yyx990803@gmail.com>
Fri, 29 May 2020 14:50:01 +0000 (10:50 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 29 May 2020 14:50:01 +0000 (10:50 -0400)
packages/runtime-core/__tests__/hmr.spec.ts
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentRenderUtils.ts
packages/runtime-core/src/componentSlots.ts
packages/runtime-core/src/hmr.ts
packages/runtime-core/src/renderer.ts

index 45d86daa5334bb1ea250f0320cdfff5a0a5fdda9..e1dab7cf60656a1c5a9f31d046865f00d393f34b 100644 (file)
@@ -45,7 +45,7 @@ describe('hot module replacement', () => {
 
     const Child: ComponentOptions = {
       __hmrId: childId,
-      render: compileToFunction(`<slot/>`)
+      render: compileToFunction(`<div><slot/></div>`)
     }
     createRecord(childId, Child)
 
@@ -62,13 +62,13 @@ describe('hot module replacement', () => {
     createRecord(parentId, Parent)
 
     render(h(Parent), root)
-    expect(serializeInner(root)).toBe(`<div>00</div>`)
+    expect(serializeInner(root)).toBe(`<div>0<div>0</div></div>`)
 
     // 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(`<div>11</div>`)
+    expect(serializeInner(root)).toBe(`<div>1<div>1</div></div>`)
 
     // // Update text while preserving state
     rerender(
@@ -77,7 +77,7 @@ describe('hot module replacement', () => {
         `<div @click="count++">{{ count }}!<Child>{{ count }}</Child></div>`
       )
     )
-    expect(serializeInner(root)).toBe(`<div>1!1</div>`)
+    expect(serializeInner(root)).toBe(`<div>1!<div>1</div></div>`)
 
     // Should force child update on slot content change
     rerender(
@@ -86,7 +86,7 @@ describe('hot module replacement', () => {
         `<div @click="count++">{{ count }}!<Child>{{ count }}!</Child></div>`
       )
     )
-    expect(serializeInner(root)).toBe(`<div>1!1!</div>`)
+    expect(serializeInner(root)).toBe(`<div>1!<div>1!</div></div>`)
 
     // Should force update element children despite block optimization
     rerender(
@@ -97,7 +97,7 @@ describe('hot module replacement', () => {
       </div>`
       )
     )
-    expect(serializeInner(root)).toBe(`<div>1<span>1</span>1!</div>`)
+    expect(serializeInner(root)).toBe(`<div>1<span>1</span><div>1!</div></div>`)
 
     // Should force update child slot elements
     rerender(
@@ -108,7 +108,7 @@ describe('hot module replacement', () => {
       </div>`
       )
     )
-    expect(serializeInner(root)).toBe(`<div><span>1</span></div>`)
+    expect(serializeInner(root)).toBe(`<div><div><span>1</span></div></div>`)
   })
 
   test('reload', async () => {
index 9ae406e336341ef773bbf06298b3675b89f2d5df..89d9916f4a11b5f16cd6941bd0fc1addd49cd323 100644 (file)
@@ -313,7 +313,7 @@ export interface ComponentInternalInstance {
    * hmr marker (dev only)
    * @internal
    */
-  renderUpdated?: boolean
+  hmrUpdated?: boolean
 }
 
 const emptyAppContext = createAppContext()
index d63f804f61f0fd14aeee4c4a51547873fe008ddb..8b4d6ed4268ebca5386a466cca638d9945b04fa2 100644 (file)
@@ -251,7 +251,7 @@ export function shouldUpdateComponent(
     __DEV__ &&
     (prevChildren || nextChildren) &&
     parentComponent &&
-    parentComponent.renderUpdated
+    parentComponent.hmrUpdated
   ) {
     return true
   }
index 6ed3ef0f09a12cd892e4d7bbf0df8a8c19411936..b26b6f17a259bac32a702168f0d666ee22441e46 100644 (file)
@@ -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.
index 0e2dfe750bdff69e4747e30a386204c99fa18131..27e5a1b6948235fb31d62a1f66f28ff29b42cde2 100644 (file)
@@ -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
   })
 }
 
index 382cd379eeda829080246b4a2eb59f2bee80850b..e289fbcb9ffba99cd3586234fef5720a52a5430c 100644 (file)
@@ -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