]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): use separate prop caches for components and mixins (#11350)
authorskirtle <65301168+skirtles-code@users.noreply.github.com>
Mon, 15 Jul 2024 13:56:37 +0000 (14:56 +0100)
committerGitHub <noreply@github.com>
Mon, 15 Jul 2024 13:56:37 +0000 (21:56 +0800)
Co-authored-by: Red Huang <gcaaa31928@gmail.com>
close #7998

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

index 8244580005bb15672428b5dd8241c901d6920da8..8c9c38b3c1fba8657609d61b07fe3aac5912b32f 100644 (file)
@@ -538,6 +538,96 @@ describe('component props', () => {
     expect(renderProxy.$props).toMatchObject(props)
   })
 
+  test('merging props from global mixins and extends', () => {
+    let renderProxy: any
+    let extendedRenderProxy: any
+
+    const defaultProp = ' from global'
+    const props = {
+      globalProp: {
+        type: String,
+        default: defaultProp,
+      },
+    }
+    const globalMixin = {
+      props,
+    }
+    const Comp = {
+      render(this: any) {
+        renderProxy = this
+        return h('div', ['Comp', this.globalProp])
+      },
+    }
+    const ExtendedComp = {
+      extends: Comp,
+      render(this: any) {
+        extendedRenderProxy = this
+        return h('div', ['ExtendedComp', this.globalProp])
+      },
+    }
+
+    const app = createApp(
+      {
+        render: () => [h(ExtendedComp), h(Comp)],
+      },
+      {},
+    )
+    app.mixin(globalMixin)
+
+    const root = nodeOps.createElement('div')
+    app.mount(root)
+
+    expect(serializeInner(root)).toMatch(
+      `<div>ExtendedComp from global</div><div>Comp from global</div>`,
+    )
+    expect(renderProxy.$props).toMatchObject({ globalProp: defaultProp })
+    expect(extendedRenderProxy.$props).toMatchObject({
+      globalProp: defaultProp,
+    })
+  })
+
+  test('merging props for a component that is also used as a mixin', () => {
+    const CompA = {
+      render(this: any) {
+        return this.foo
+      },
+    }
+
+    const mixin = {
+      props: {
+        foo: {
+          default: 'from mixin',
+        },
+      },
+    }
+
+    const CompB = {
+      mixins: [mixin, CompA],
+      render(this: any) {
+        return this.foo
+      },
+    }
+
+    const app = createApp({
+      render() {
+        return [h(CompA), ', ', h(CompB)]
+      },
+    })
+
+    app.mixin({
+      props: {
+        foo: {
+          default: 'from global mixin',
+        },
+      },
+    })
+
+    const root = nodeOps.createElement('div')
+    app.mount(root)
+
+    expect(serializeInner(root)).toMatch(`from global mixin, from mixin`)
+  })
+
   test('props type support BigInt', () => {
     const Comp = {
       props: {
index 5a4292b6f36d8630f0b23bf23c2399ac8ad52677..9d7b7f0e4a52ce49a4544241ee66d07e14459c21 100644 (file)
@@ -496,12 +496,15 @@ function resolvePropValue(
   return value
 }
 
+const mixinPropsCache = new WeakMap<ConcreteComponent, NormalizedPropsOptions>()
+
 export function normalizePropsOptions(
   comp: ConcreteComponent,
   appContext: AppContext,
   asMixin = false,
 ): NormalizedPropsOptions {
-  const cache = appContext.propsCache
+  const cache =
+    __FEATURE_OPTIONS_API__ && asMixin ? mixinPropsCache : appContext.propsCache
   const cached = cache.get(comp)
   if (cached) {
     return cached