]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): ensure declare prop keys are always present
authorEvan You <yyx990803@gmail.com>
Fri, 2 Apr 2021 00:25:12 +0000 (20:25 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 2 Apr 2021 00:25:12 +0000 (20:25 -0400)
fix #3288

packages/runtime-core/__tests__/componentProps.spec.ts
packages/runtime-core/src/componentProps.ts

index a97c9d48190c1570158ce186a528fb421e6dc4c9..658ef5c567acc636883b5ad60011d38cd7c29d3e 100644 (file)
@@ -11,7 +11,8 @@ import {
   createApp,
   provide,
   inject,
-  watch
+  watch,
+  toRefs
 } from '@vue/runtime-test'
 import { render as domRender, nextTick } from 'vue'
 
@@ -479,4 +480,32 @@ describe('component props', () => {
     expect(serializeInner(root)).toMatch(`<h1>11</h1>`)
     expect(count).toBe(0)
   })
+
+  // #3288
+  test('declared prop key should be present even if not passed', async () => {
+    let initialKeys: string[] = []
+    const changeSpy = jest.fn()
+    const passFoo = ref(false)
+
+    const Comp = {
+      render() {},
+      props: {
+        foo: String
+      },
+      setup(props: any) {
+        initialKeys = Object.keys(props)
+        const { foo } = toRefs(props)
+        watch(foo, changeSpy)
+      }
+    }
+
+    const Parent = () => (passFoo.value ? h(Comp, { foo: 'ok' }) : h(Comp))
+    const root = nodeOps.createElement('div')
+    createApp(Parent).mount(root)
+
+    expect(initialKeys).toMatchObject(['foo'])
+    passFoo.value = true
+    await nextTick()
+    expect(changeSpy).toHaveBeenCalledTimes(1)
+  })
 })
index 476fba887dce8a156ebe219f196439f5e703b83d..0238bf80e2809a2e9e892ca1ef25b1c7328d78bf 100644 (file)
@@ -143,6 +143,14 @@ export function initProps(
   instance.propsDefaults = Object.create(null)
 
   setFullProps(instance, rawProps, props, attrs)
+
+  // ensure all declared prop keys are present
+  for (const key in instance.propsOptions[0]) {
+    if (!(key in props)) {
+      props[key] = undefined
+    }
+  }
+
   // validation
   if (__DEV__) {
     validateProps(rawProps || {}, props, instance)
@@ -281,11 +289,11 @@ function setFullProps(
   const [options, needCastKeys] = instance.propsOptions
   if (rawProps) {
     for (const key in rawProps) {
-      const value = rawProps[key]
       // key, ref are reserved and never passed down
       if (isReservedProp(key)) {
         continue
       }
+      const value = rawProps[key]
       // prop option names are camelized during normalization, so to support
       // kebab -> camel conversion here we need to camelize the key.
       let camelKey