Explorar el Código

fix(runtime-vapor): enable injection from VDOM parent to slotted Vapor child (#14167)

edison hace 4 meses
padre
commit
2f0676f1ed

+ 7 - 1
packages/runtime-core/src/apiCreateApp.ts

@@ -192,7 +192,13 @@ export interface VaporInteropInterface {
   update(n1: VNode, n2: VNode, shouldUpdate: boolean): void
   unmount(vnode: VNode, doRemove?: boolean): void
   move(vnode: VNode, container: any, anchor: any): void
-  slot(n1: VNode | null, n2: VNode, container: any, anchor: any): void
+  slot(
+    n1: VNode | null,
+    n2: VNode,
+    container: any,
+    anchor: any,
+    parentComponent: ComponentInternalInstance | null,
+  ): void
   hydrate(
     vnode: VNode,
     node: any,

+ 7 - 1
packages/runtime-core/src/renderer.ts

@@ -454,7 +454,13 @@ function baseCreateRenderer(
         )
         break
       case VaporSlot:
-        getVaporInterface(parentComponent, n2).slot(n1, n2, container, anchor)
+        getVaporInterface(parentComponent, n2).slot(
+          n1,
+          n2,
+          container,
+          anchor,
+          parentComponent,
+        )
         break
       default:
         if (shapeFlag & ShapeFlags.ELEMENT) {

+ 49 - 1
packages/runtime-vapor/__tests__/apiInject.spec.ts

@@ -9,6 +9,7 @@ import {
   reactive,
   readonly,
   ref,
+  renderSlot,
   toDisplayString,
 } from '@vue/runtime-dom'
 import {
@@ -18,11 +19,12 @@ import {
   createVaporApp,
   defineVaporComponent,
   renderEffect,
+  template,
   vaporInteropPlugin,
   withVaporCtx,
 } from '../src'
 import { makeRender } from './_utils'
-import { setElementText } from '../src/dom/prop'
+import { setElementText, setText } from '../src/dom/prop'
 
 const define = makeRender<any>()
 
@@ -426,6 +428,10 @@ describe('api: provide/inject', () => {
 })
 
 describe('vdom interop', () => {
+  beforeEach(() => {
+    document.body.innerHTML = ''
+  })
+
   test('should inject value from vapor parent', async () => {
     const VdomChild = {
       setup() {
@@ -453,5 +459,47 @@ describe('vdom interop', () => {
     value.value = 'bar'
     await nextTick()
     expect(root.innerHTML).toBe('<div>bar</div>')
+
+    app.unmount()
+  })
+
+  test('slotted vapor child should inject value from vdom parent', async () => {
+    const value = ref('foo')
+    const VdomParent = {
+      setup(_: any, { slots }: any) {
+        provide('foo', value)
+        return () => renderSlot(slots, 'default')
+      },
+    }
+
+    const VaporChild = defineVaporComponent({
+      setup() {
+        const foo = inject('foo')
+        const n0 = template(' ')() as any
+        renderEffect(() => setText(n0, toDisplayString(foo)))
+        return n0
+      },
+    })
+
+    const App = defineVaporComponent({
+      setup() {
+        return createComponent(VdomParent, null, {
+          default: () => createComponent(VaporChild),
+        })
+      },
+    })
+
+    const root = document.createElement('div')
+    document.body.appendChild(root)
+    const app = createVaporApp(App)
+    app.use(vaporInteropPlugin)
+    app.mount(root)
+
+    expect(root.innerHTML).toBe('foo')
+
+    value.value = 'bar'
+    await nextTick()
+    expect(root.innerHTML).toBe('bar')
+    app.unmount()
   })
 })

+ 4 - 1
packages/runtime-vapor/src/vdomInterop.ts

@@ -185,8 +185,10 @@ const vaporInteropImpl: Omit<
   /**
    * vapor slot in vdom
    */
-  slot(n1: VNode, n2: VNode, container, anchor) {
+  slot(n1: VNode, n2: VNode, container, anchor, parentComponent) {
     if (!n1) {
+      const prev = currentInstance
+      simpleSetCurrentInstance(parentComponent)
       // mount
       let selfAnchor: Node | undefined
       const { slot, fallback } = n2.vs!
@@ -198,6 +200,7 @@ const vaporInteropImpl: Omit<
         // use fragment's anchor when possible
         selfAnchor = slotBlock.anchor
       }
+      simpleSetCurrentInstance(prev)
       if (!selfAnchor) selfAnchor = createTextNode()
       insert((n2.el = n2.anchor = selfAnchor), container, anchor)
       insert((n2.vb = slotBlock), container, selfAnchor)