Преглед изворни кода

fix(slots): properly warn if slot invoked in setup (#12195)

close #12194
yangxiuxiu пре 1 година
родитељ
комит
9196222ae1

+ 94 - 0
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()
+  })
 })

+ 7 - 2
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: ` +