From: yangxiuxiu <79584569+yangxiuxiu1115@users.noreply.github.com> Date: Fri, 2 May 2025 09:53:14 +0000 (+0800) Subject: fix(slots): properly warn if slot invoked in setup (#12195) X-Git-Tag: v3.5.14~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9196222ae1d63b52b35ac5fbf5e71494587ccf05;p=thirdparty%2Fvuejs%2Fcore.git fix(slots): properly warn if slot invoked in setup (#12195) close #12194 --- diff --git a/packages/runtime-core/__tests__/componentSlots.spec.ts b/packages/runtime-core/__tests__/componentSlots.spec.ts index 93d88ad0e4..2cf50b964b 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 44246add9b..3812695431 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' @@ -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: ` +