mirror of https://github.com/vuejs/core.git
parent
9b5a34bf8c
commit
94fa67a4f7
|
@ -537,4 +537,35 @@ describe('hot module replacement', () => {
|
|||
render(h(Foo), root)
|
||||
expect(serializeInner(root)).toBe('bar')
|
||||
})
|
||||
|
||||
// #7155 - force HMR on slots content update
|
||||
test('force update slot content change', () => {
|
||||
const root = nodeOps.createElement('div')
|
||||
const parentId = 'test-force-computed-parent'
|
||||
const childId = 'test-force-computed-child'
|
||||
|
||||
const Child: ComponentOptions = {
|
||||
__hmrId: childId,
|
||||
computed: {
|
||||
slotContent() {
|
||||
return this.$slots.default?.()
|
||||
}
|
||||
},
|
||||
render: compileToFunction(`<component :is="() => slotContent" />`)
|
||||
}
|
||||
createRecord(childId, Child)
|
||||
|
||||
const Parent: ComponentOptions = {
|
||||
__hmrId: parentId,
|
||||
components: { Child },
|
||||
render: compileToFunction(`<Child>1</Child>`)
|
||||
}
|
||||
createRecord(parentId, Parent)
|
||||
|
||||
render(h(Parent), root)
|
||||
expect(serializeInner(root)).toBe(`1`)
|
||||
|
||||
rerender(parentId, compileToFunction(`<Child>2</Child>`))
|
||||
expect(serializeInner(root)).toBe(`2`)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -349,6 +349,10 @@ export interface ComponentInternalInstance {
|
|||
slots: InternalSlots
|
||||
refs: Data
|
||||
emit: EmitFn
|
||||
|
||||
attrsProxy: Data | null
|
||||
slotsProxy: Slots | null
|
||||
|
||||
/**
|
||||
* used for keeping track of .once event handlers on components
|
||||
* @internal
|
||||
|
@ -536,6 +540,9 @@ export function createComponentInstance(
|
|||
setupState: EMPTY_OBJ,
|
||||
setupContext: null,
|
||||
|
||||
attrsProxy: null,
|
||||
slotsProxy: null,
|
||||
|
||||
// suspense related
|
||||
suspense,
|
||||
suspenseId: suspense ? suspense.pendingId : 0,
|
||||
|
@ -923,8 +930,10 @@ export function finishComponentSetup(
|
|||
}
|
||||
}
|
||||
|
||||
function createAttrsProxy(instance: ComponentInternalInstance): Data {
|
||||
return new Proxy(
|
||||
function getAttrsProxy(instance: ComponentInternalInstance): Data {
|
||||
return (
|
||||
instance.attrsProxy ||
|
||||
(instance.attrsProxy = new Proxy(
|
||||
instance.attrs,
|
||||
__DEV__
|
||||
? {
|
||||
|
@ -948,6 +957,30 @@ function createAttrsProxy(instance: ComponentInternalInstance): Data {
|
|||
return target[key]
|
||||
}
|
||||
}
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dev-only
|
||||
*/
|
||||
function getSlotsProxy(instance: ComponentInternalInstance): Slots {
|
||||
return (
|
||||
instance.slotsProxy ||
|
||||
(instance.slotsProxy = new Proxy(instance.slots, {
|
||||
get(target, key: string) {
|
||||
track(instance, TrackOpTypes.GET, '$slots')
|
||||
return target[key]
|
||||
},
|
||||
set() {
|
||||
warn(`setupContext.slots is readonly.`)
|
||||
return false
|
||||
},
|
||||
deleteProperty() {
|
||||
warn(`setupContext.slots is readonly.`)
|
||||
return false
|
||||
}
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -978,16 +1011,15 @@ export function createSetupContext(
|
|||
instance.exposed = exposed || {}
|
||||
}
|
||||
|
||||
let attrs: Data
|
||||
if (__DEV__) {
|
||||
// We use getters in dev in case libs like test-utils overwrite instance
|
||||
// properties (overwrites should not be done in prod)
|
||||
return Object.freeze({
|
||||
get attrs() {
|
||||
return attrs || (attrs = createAttrsProxy(instance))
|
||||
return getAttrsProxy(instance)
|
||||
},
|
||||
get slots() {
|
||||
return shallowReadonly(instance.slots)
|
||||
return getSlotsProxy(instance)
|
||||
},
|
||||
get emit() {
|
||||
return (event: string, ...args: any[]) => instance.emit(event, ...args)
|
||||
|
@ -997,7 +1029,7 @@ export function createSetupContext(
|
|||
} else {
|
||||
return {
|
||||
get attrs() {
|
||||
return attrs || (attrs = createAttrsProxy(instance))
|
||||
return getAttrsProxy(instance)
|
||||
},
|
||||
slots: instance.slots,
|
||||
emit: instance.emit,
|
||||
|
|
|
@ -356,6 +356,8 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||
if (key === '$attrs') {
|
||||
track(instance, TrackOpTypes.GET, key)
|
||||
__DEV__ && markAttrsAccessed()
|
||||
} else if (__DEV__ && key === '$slots') {
|
||||
track(instance, TrackOpTypes.GET, key)
|
||||
}
|
||||
return publicGetter(instance)
|
||||
} else if (
|
||||
|
|
|
@ -23,6 +23,8 @@ import { ContextualRenderFn, withCtx } from './componentRenderContext'
|
|||
import { isHmrUpdating } from './hmr'
|
||||
import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig'
|
||||
import { toRaw } from '@vue/reactivity'
|
||||
import { trigger } from '@vue/reactivity'
|
||||
import { TriggerOpTypes } from '@vue/reactivity'
|
||||
|
||||
export type Slot<T extends any = any> = (
|
||||
...args: IfAny<T, any[], [T] | (T extends undefined ? [] : never)>
|
||||
|
@ -196,6 +198,7 @@ export const updateSlots = (
|
|||
// 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)
|
||||
trigger(instance, TriggerOpTypes.SET, '$slots')
|
||||
} else if (optimized && type === SlotFlags.STABLE) {
|
||||
// compiled AND stable.
|
||||
// no need to update, and skip stale slots removal.
|
||||
|
|
Loading…
Reference in New Issue