fix(runtime-core): ensure setupContext.attrs reactivity when used in child slots
fix #4161
This commit is contained in:
parent
ff0c810300
commit
8560005601
@ -135,6 +135,44 @@ describe('api: setup context', () => {
|
|||||||
expect(serializeInner(root)).toMatch(`<div class="baz"></div>`)
|
expect(serializeInner(root)).toMatch(`<div class="baz"></div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #4161
|
||||||
|
it('context.attrs in child component slots', async () => {
|
||||||
|
const toggle = ref(true)
|
||||||
|
|
||||||
|
const Parent = {
|
||||||
|
render: () => h(Child, toggle.value ? { id: 'foo' } : { class: 'baz' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = {
|
||||||
|
render(this: any) {
|
||||||
|
return this.$slots.default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Child = {
|
||||||
|
inheritAttrs: false,
|
||||||
|
setup(_: any, { attrs }: any) {
|
||||||
|
return () => {
|
||||||
|
const vnode = h(Wrapper, null, {
|
||||||
|
default: () => [h('div', attrs)],
|
||||||
|
_: 1 // mark stable slots
|
||||||
|
})
|
||||||
|
vnode.dynamicChildren = [] // force optimized mode
|
||||||
|
return vnode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
render(h(Parent), root)
|
||||||
|
expect(serializeInner(root)).toMatch(`<div id="foo"></div>`)
|
||||||
|
|
||||||
|
// should update even though it's not reactive
|
||||||
|
toggle.value = false
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toMatch(`<div class="baz"></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
it('context.slots', async () => {
|
it('context.slots', async () => {
|
||||||
const id = ref('foo')
|
const id = ref('foo')
|
||||||
|
|
||||||
|
@ -5,7 +5,9 @@ import {
|
|||||||
shallowReadonly,
|
shallowReadonly,
|
||||||
proxyRefs,
|
proxyRefs,
|
||||||
EffectScope,
|
EffectScope,
|
||||||
markRaw
|
markRaw,
|
||||||
|
track,
|
||||||
|
TrackOpTypes
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
import {
|
import {
|
||||||
ComponentPublicInstance,
|
ComponentPublicInstance,
|
||||||
@ -834,19 +836,32 @@ export function finishComponentSetup(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const attrDevProxyHandlers: ProxyHandler<Data> = {
|
function createAttrsProxy(instance: ComponentInternalInstance): Data {
|
||||||
get: (target, key: string) => {
|
return new Proxy(
|
||||||
|
instance.attrs,
|
||||||
|
__DEV__
|
||||||
|
? {
|
||||||
|
get(target, key: string) {
|
||||||
markAttrsAccessed()
|
markAttrsAccessed()
|
||||||
|
track(instance, TrackOpTypes.GET, '$attrs')
|
||||||
return target[key]
|
return target[key]
|
||||||
},
|
},
|
||||||
set: () => {
|
set() {
|
||||||
warn(`setupContext.attrs is readonly.`)
|
warn(`setupContext.attrs is readonly.`)
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
deleteProperty: () => {
|
deleteProperty() {
|
||||||
warn(`setupContext.attrs is readonly.`)
|
warn(`setupContext.attrs is readonly.`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
get(target, key: string) {
|
||||||
|
track(instance, TrackOpTypes.GET, '$attrs')
|
||||||
|
return target[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSetupContext(
|
export function createSetupContext(
|
||||||
@ -859,15 +874,13 @@ export function createSetupContext(
|
|||||||
instance.exposed = exposed || {}
|
instance.exposed = exposed || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__) {
|
|
||||||
let attrs: Data
|
let attrs: Data
|
||||||
|
if (__DEV__) {
|
||||||
// We use getters in dev in case libs like test-utils overwrite instance
|
// We use getters in dev in case libs like test-utils overwrite instance
|
||||||
// properties (overwrites should not be done in prod)
|
// properties (overwrites should not be done in prod)
|
||||||
return Object.freeze({
|
return Object.freeze({
|
||||||
get attrs() {
|
get attrs() {
|
||||||
return (
|
return attrs || (attrs = createAttrsProxy(instance))
|
||||||
attrs || (attrs = new Proxy(instance.attrs, attrDevProxyHandlers))
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
get slots() {
|
get slots() {
|
||||||
return shallowReadonly(instance.slots)
|
return shallowReadonly(instance.slots)
|
||||||
@ -879,7 +892,9 @@ export function createSetupContext(
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
attrs: instance.attrs,
|
get attrs() {
|
||||||
|
return attrs || (attrs = createAttrsProxy(instance))
|
||||||
|
},
|
||||||
slots: instance.slots,
|
slots: instance.slots,
|
||||||
emit: instance.emit,
|
emit: instance.emit,
|
||||||
expose
|
expose
|
||||||
|
Loading…
x
Reference in New Issue
Block a user