]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(slots): properly warn if slot invoked in setup (#12195)
authoryangxiuxiu <79584569+yangxiuxiu1115@users.noreply.github.com>
Fri, 2 May 2025 09:53:14 +0000 (17:53 +0800)
committerGitHub <noreply@github.com>
Fri, 2 May 2025 09:53:14 +0000 (02:53 -0700)
close #12194

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

index 93d88ad0e4bcf2db36fad102a484d9a9f8c35157..2cf50b964bf5523184e8ed7a5bc7cc773f807441 100644 (file)
@@ -324,4 +324,98 @@ describe('component: slots', () => {
       'Slot "default" invoked outside of the render function',
     ).not.toHaveBeenWarned()
   })
+
+  test('basic warn', () => {
+    const Comp = {
+      setup(_: any, { slots }: any) {
+        slots.default && slots.default()
+        return () => null
+      },
+    }
+
+    const App = {
+      setup() {
+        return () => h(Comp, () => h('div'))
+      },
+    }
+
+    createApp(App).mount(nodeOps.createElement('div'))
+    expect(
+      'Slot "default" invoked outside of the render function',
+    ).toHaveBeenWarned()
+  })
+
+  test('basic warn when mounting another app in setup', () => {
+    const Comp = {
+      setup(_: any, { slots }: any) {
+        slots.default?.()
+        return () => null
+      },
+    }
+
+    const mountComp = () => {
+      createApp({
+        setup() {
+          return () => h(Comp, () => 'msg')
+        },
+      }).mount(nodeOps.createElement('div'))
+    }
+
+    const App = {
+      setup() {
+        mountComp()
+        return () => null
+      },
+    }
+
+    createApp(App).mount(nodeOps.createElement('div'))
+    expect(
+      'Slot "default" invoked outside of the render function',
+    ).toHaveBeenWarned()
+  })
+
+  test('should not warn when render in setup', () => {
+    const container = {
+      setup(_: any, { slots }: any) {
+        return () => slots.default && slots.default()
+      },
+    }
+
+    const comp = h(container, null, () => h('div'))
+
+    const App = {
+      setup() {
+        render(h(comp), nodeOps.createElement('div'))
+        return () => null
+      },
+    }
+
+    createApp(App).mount(nodeOps.createElement('div'))
+    expect(
+      'Slot "default" invoked outside of the render function',
+    ).not.toHaveBeenWarned()
+  })
+
+  test('basic warn when render in setup', () => {
+    const container = {
+      setup(_: any, { slots }: any) {
+        slots.default && slots.default()
+        return () => null
+      },
+    }
+
+    const comp = h(container, null, () => h('div'))
+
+    const App = {
+      setup() {
+        render(h(comp), nodeOps.createElement('div'))
+        return () => null
+      },
+    }
+
+    createApp(App).mount(nodeOps.createElement('div'))
+    expect(
+      'Slot "default" invoked outside of the render function',
+    ).toHaveBeenWarned()
+  })
 })
index 44246add9b94033f640e3e257d5ff87276b26633..3812695431e53835fa25d731e89d8bd8629e4f75 100644 (file)
@@ -17,7 +17,11 @@ import {
 } from '@vue/shared'
 import { warn } from './warning'
 import { isKeepAlive } from './components/KeepAlive'
-import { type ContextualRenderFn, withCtx } from './componentRenderContext'
+import {
+  type ContextualRenderFn,
+  currentRenderingInstance,
+  withCtx,
+} from './componentRenderContext'
 import { isHmrUpdating } from './hmr'
 import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig'
 import { TriggerOpTypes, trigger } from '@vue/reactivity'
@@ -102,7 +106,8 @@ const normalizeSlot = (
     if (
       __DEV__ &&
       currentInstance &&
-      (!ctx || ctx.root === currentInstance.root)
+      !(ctx === null && currentRenderingInstance) &&
+      !(ctx && ctx.root !== currentInstance.root)
     ) {
       warn(
         `Slot "${key}" invoked outside of the render function: ` +