diff --git a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts
index 3493ec117f3..7ef9b62a36d 100644
--- a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts
+++ b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts
@@ -640,6 +640,74 @@ describe('vdomInterop', () => {
'
boolean:false:false
boolean:false:false
',
)
})
+
+ test('vnode props of a vdom component are a plain object', async () => {
+ // e.g. Vuetify's useProxiedModel checks `vm.vnode.props?.hasOwnProperty()`
+ const Field = defineComponent({
+ props: ['modelValue'],
+ setup(props) {
+ const vm = getCurrentInstance()!
+ expect(vm.vnode.props!.hasOwnProperty('modelValue')).toBe(true)
+ expect(vm.vnode.props!.hasOwnProperty('onUpdate:modelValue')).toBe(
+ true,
+ )
+ return () =>
+ h(
+ 'i',
+ `${props.modelValue}:${vm.vnode.props!.hasOwnProperty('modelValue')}:${vm.vnode.props!.hasOwnProperty('onUpdate:modelValue')}`,
+ )
+ },
+ })
+ const { vdom, vapor } = await renderParity(
+ {
+ App: ``,
+ },
+ () => ref({ n: 1 }),
+ async data => {
+ data.value.n++
+ },
+ { Field },
+ )
+ expect(vdom.after).toBe('2:true:true')
+ expect(vapor.after).toBe(vdom.after)
+ })
+
+ test.each([null, { inherited: true }, 'value'])(
+ 'preserves an own __proto__ vnode prop with value %j',
+ async value => {
+ let vm: ReturnType
+ const Child = defineComponent({
+ inheritAttrs: false,
+ setup() {
+ vm = getCurrentInstance()!
+ return () => null
+ },
+ })
+ await renderParity(
+ {
+ App: ``,
+ },
+ () => shallowRef({ key: '__proto__', value }),
+ async data => {
+ expect(Object.getPrototypeOf(vm!.vnode.props)).toBe(
+ Object.prototype,
+ )
+ expect(vm!.vnode.props!.hasOwnProperty('__proto__')).toBe(true)
+ expect(vm!.vnode.props!.__proto__).toBe(value)
+
+ data.value = { key: '__proto__', value: { updated: true } }
+ await nextTick()
+
+ expect(Object.getPrototypeOf(vm!.vnode.props)).toBe(
+ Object.prototype,
+ )
+ expect(vm!.vnode.props!.hasOwnProperty('__proto__')).toBe(true)
+ expect(vm!.vnode.props!.__proto__).toBe(data.value.value)
+ },
+ { Child },
+ )
+ },
+ )
})
describe('v-model', () => {
diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts
index c4ceada475d..d8138aba944 100644
--- a/packages/runtime-vapor/src/componentProps.ts
+++ b/packages/runtime-vapor/src/componentProps.ts
@@ -811,7 +811,8 @@ function updateProps(
triggerPropsValue(instance, RAW_VALUES_KEY)
}
const vnode = isInteropEnabled && instance.interopVNode
- if (vnode && vnode.vi) vnode.props = rawValues
+ // vdom code reads vnode props as a plain object (e.g. `props.hasOwnProperty`)
+ if (vnode && vnode.vi) vnode.props = { ...rawValues }
const present: Record | undefined =
propsToUpdate || isInitial ? undefined : Object.create(null)