diff --git a/packages/runtime-core/__tests__/componentSlots.spec.ts b/packages/runtime-core/__tests__/componentSlots.spec.ts index 93d88ad0e4b..2cf50b964bf 100644 --- a/packages/runtime-core/__tests__/componentSlots.spec.ts +++ b/packages/runtime-core/__tests__/componentSlots.spec.ts @@ -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() + }) }) diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index 8f8392f1cdb..1e416cfbead 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -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' @@ -97,7 +101,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: ` +