Przeglądaj źródła

fix(runtime-vapor): stop stale slot outlet effects on unmount

daiwei 1 miesiąc temu
rodzic
commit
cb05b9a154

+ 40 - 0
packages/runtime-vapor/__tests__/vdomInterop.spec.ts

@@ -1286,6 +1286,46 @@ describe('vdomInterop', () => {
       expect(html()).toBe('<div><span>two</span></div>')
       expect(firstState.runs).toHaveBeenCalledTimes(1)
     })
+
+    test('should stop vdom slot outlet effects after outlet unmount', async () => {
+      const showOutlet = ref(true)
+      const msg = ref('one')
+      const track = vi.fn()
+
+      const VaporChild = defineVaporComponent({
+        setup() {
+          return createIf(
+            () => showOutlet.value,
+            () => createSlot('default'),
+          )
+        },
+      })
+
+      const { html } = define({
+        setup() {
+          return () =>
+            h(VaporChild as any, null, {
+              default: () => {
+                track()
+                return [h('span', msg.value)]
+              },
+            })
+        },
+      }).render()
+
+      expect(html()).toBe('<span>one</span><!--if-->')
+      expect(track).toHaveBeenCalledTimes(1)
+
+      showOutlet.value = false
+      await nextTick()
+      expect(html()).toBe('<!--if-->')
+
+      msg.value = 'two'
+      await nextTick()
+
+      expect(track).toHaveBeenCalledTimes(1)
+      expect(html()).toBe('<!--if-->')
+    })
   })
 
   describe('provide / inject', () => {

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

@@ -959,6 +959,7 @@ function renderVDOMSlot(
   let currentVNode: VNode | null = null
   let currentParentNode: ParentNode | undefined
   let currentAnchor: Node | null | undefined
+  let scope = effectScope()
 
   frag.insert = (parentNode, anchor) => {
     if (isHydrating) return
@@ -966,7 +967,7 @@ function renderVDOMSlot(
     currentAnchor = anchor
 
     if (!isMounted) {
-      render()
+      scope.run(render)
       isMounted = true
     } else {
       if (currentVNode) {
@@ -988,6 +989,7 @@ function renderVDOMSlot(
   }
 
   frag.remove = parentNode => {
+    scope.stop()
     if (currentBlock) {
       remove(currentBlock, parentNode)
     } else if (currentVNode) {
@@ -1132,7 +1134,7 @@ function renderVDOMSlot(
 
   frag.hydrate = () => {
     if (!isHydrating) return
-    render()
+    scope.run(render)
     isMounted = true
   }