fix(hmr): force full update in child component on slot update
This commit is contained in:
parent
5ddd9d2417
commit
2408a65662
@ -45,7 +45,7 @@ describe('hot module replacement', () => {
|
|||||||
|
|
||||||
const Child: ComponentOptions = {
|
const Child: ComponentOptions = {
|
||||||
__hmrId: childId,
|
__hmrId: childId,
|
||||||
render: compileToFunction(`<slot/>`)
|
render: compileToFunction(`<div><slot/></div>`)
|
||||||
}
|
}
|
||||||
createRecord(childId, Child)
|
createRecord(childId, Child)
|
||||||
|
|
||||||
@ -62,13 +62,13 @@ describe('hot module replacement', () => {
|
|||||||
createRecord(parentId, Parent)
|
createRecord(parentId, Parent)
|
||||||
|
|
||||||
render(h(Parent), root)
|
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
|
// Perform some state change. This change should be preserved after the
|
||||||
// re-render!
|
// re-render!
|
||||||
triggerEvent(root.children[0] as TestElement, 'click')
|
triggerEvent(root.children[0] as TestElement, 'click')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(serializeInner(root)).toBe(`<div>11</div>`)
|
expect(serializeInner(root)).toBe(`<div>1<div>1</div></div>`)
|
||||||
|
|
||||||
// // Update text while preserving state
|
// // Update text while preserving state
|
||||||
rerender(
|
rerender(
|
||||||
@ -77,7 +77,7 @@ describe('hot module replacement', () => {
|
|||||||
`<div @click="count++">{{ count }}!<Child>{{ count }}</Child></div>`
|
`<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
|
// Should force child update on slot content change
|
||||||
rerender(
|
rerender(
|
||||||
@ -86,7 +86,7 @@ describe('hot module replacement', () => {
|
|||||||
`<div @click="count++">{{ count }}!<Child>{{ count }}!</Child></div>`
|
`<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
|
// Should force update element children despite block optimization
|
||||||
rerender(
|
rerender(
|
||||||
@ -97,7 +97,7 @@ describe('hot module replacement', () => {
|
|||||||
</div>`
|
</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
|
// Should force update child slot elements
|
||||||
rerender(
|
rerender(
|
||||||
@ -108,7 +108,7 @@ describe('hot module replacement', () => {
|
|||||||
</div>`
|
</div>`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
expect(serializeInner(root)).toBe(`<div><span>1</span></div>`)
|
expect(serializeInner(root)).toBe(`<div><div><span>1</span></div></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('reload', async () => {
|
test('reload', async () => {
|
||||||
|
@ -313,7 +313,7 @@ export interface ComponentInternalInstance {
|
|||||||
* hmr marker (dev only)
|
* hmr marker (dev only)
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
renderUpdated?: boolean
|
hmrUpdated?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyAppContext = createAppContext()
|
const emptyAppContext = createAppContext()
|
||||||
|
@ -251,7 +251,7 @@ export function shouldUpdateComponent(
|
|||||||
__DEV__ &&
|
__DEV__ &&
|
||||||
(prevChildren || nextChildren) &&
|
(prevChildren || nextChildren) &&
|
||||||
parentComponent &&
|
parentComponent &&
|
||||||
parentComponent.renderUpdated
|
parentComponent.hmrUpdated
|
||||||
) {
|
) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { isKeepAlive } from './components/KeepAlive'
|
import { isKeepAlive } from './components/KeepAlive'
|
||||||
import { withCtx } from './helpers/withRenderContext'
|
import { withCtx } from './helpers/withRenderContext'
|
||||||
|
import { queuePostFlushCb } from './scheduler'
|
||||||
|
|
||||||
export type Slot = (...args: any[]) => VNode[]
|
export type Slot = (...args: any[]) => VNode[]
|
||||||
|
|
||||||
@ -124,11 +125,17 @@ export const updateSlots = (
|
|||||||
if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
|
if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
|
||||||
if ((children as RawSlots)._ === 1) {
|
if ((children as RawSlots)._ === 1) {
|
||||||
// compiled slots.
|
// 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)
|
// bail on dynamic slots (v-if, v-for, reference of scope variables)
|
||||||
!(vnode.patchFlag & PatchFlags.DYNAMIC_SLOTS) &&
|
!(vnode.patchFlag & PatchFlags.DYNAMIC_SLOTS)
|
||||||
// bail on HRM updates
|
|
||||||
!(__DEV__ && instance.parent && instance.parent.renderUpdated)
|
|
||||||
) {
|
) {
|
||||||
// compiled AND static.
|
// compiled AND static.
|
||||||
// no need to update, and skip stale slots removal.
|
// no need to update, and skip stale slots removal.
|
||||||
|
@ -70,9 +70,9 @@ function rerender(id: string, newRender?: Function) {
|
|||||||
}
|
}
|
||||||
instance.renderCache = []
|
instance.renderCache = []
|
||||||
// this flag forces child components with slot content to update
|
// this flag forces child components with slot content to update
|
||||||
instance.renderUpdated = true
|
instance.hmrUpdated = true
|
||||||
instance.update()
|
instance.update()
|
||||||
instance.renderUpdated = false
|
instance.hmrUpdated = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -779,7 +779,7 @@ function baseCreateRenderer(
|
|||||||
invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')
|
invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__ && parentComponent && parentComponent.renderUpdated) {
|
if (__DEV__ && parentComponent && parentComponent.hmrUpdated) {
|
||||||
// HMR updated, force full diff
|
// HMR updated, force full diff
|
||||||
patchFlag = 0
|
patchFlag = 0
|
||||||
optimized = false
|
optimized = false
|
||||||
@ -1006,7 +1006,7 @@ function baseCreateRenderer(
|
|||||||
optimized = true
|
optimized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__ && parentComponent && parentComponent.renderUpdated) {
|
if (__DEV__ && parentComponent && parentComponent.hmrUpdated) {
|
||||||
// HMR updated, force full diff
|
// HMR updated, force full diff
|
||||||
patchFlag = 0
|
patchFlag = 0
|
||||||
optimized = false
|
optimized = false
|
||||||
|
Loading…
Reference in New Issue
Block a user