2
0
Эх сурвалжийг харах

fix(compiler-core): remove slot cache from parent renderCache during unmounting (#13215)

* fix(compiler-core): remove slot cache from parent renderCache during unmounting

* chore: update
edison 1 жил өмнө
parent
commit
5d166f3796

+ 10 - 0
packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts

@@ -170,6 +170,11 @@ describe('compiler: cacheStatic transform', () => {
         {
           /* _ slot flag */
         },
+        {
+          type: NodeTypes.JS_PROPERTY,
+          key: { content: '__' },
+          value: { content: '[0]' },
+        },
       ],
     })
   })
@@ -197,6 +202,11 @@ describe('compiler: cacheStatic transform', () => {
         {
           /* _ slot flag */
         },
+        {
+          type: NodeTypes.JS_PROPERTY,
+          key: { content: '__' },
+          value: { content: '[0]' },
+        },
       ],
     })
   })

+ 27 - 0
packages/compiler-core/src/transforms/cacheStatic.ts

@@ -12,11 +12,14 @@ import {
   type RootNode,
   type SimpleExpressionNode,
   type SlotFunctionExpression,
+  type SlotsObjectProperty,
   type TemplateChildNode,
   type TemplateNode,
   type TextCallNode,
   type VNodeCall,
   createArrayExpression,
+  createObjectProperty,
+  createSimpleExpression,
   getVNodeBlockHelper,
   getVNodeHelper,
 } from '../ast'
@@ -140,6 +143,7 @@ function walk(
   }
 
   let cachedAsArray = false
+  const slotCacheKeys = []
   if (toCache.length === children.length && node.type === NodeTypes.ELEMENT) {
     if (
       node.tagType === ElementTypes.ELEMENT &&
@@ -163,6 +167,7 @@ function walk(
       // default slot
       const slot = getSlotNode(node.codegenNode, 'default')
       if (slot) {
+        slotCacheKeys.push(context.cached.length)
         slot.returns = getCacheExpression(
           createArrayExpression(slot.returns as TemplateChildNode[]),
         )
@@ -186,6 +191,7 @@ function walk(
         slotName.arg &&
         getSlotNode(parent.codegenNode, slotName.arg)
       if (slot) {
+        slotCacheKeys.push(context.cached.length)
         slot.returns = getCacheExpression(
           createArrayExpression(slot.returns as TemplateChildNode[]),
         )
@@ -196,10 +202,31 @@ function walk(
 
   if (!cachedAsArray) {
     for (const child of toCache) {
+      slotCacheKeys.push(context.cached.length)
       child.codegenNode = context.cache(child.codegenNode!)
     }
   }
 
+  // put the slot cached keys on the slot object, so that the cache
+  // can be removed when component unmounting to prevent memory leaks
+  if (
+    slotCacheKeys.length &&
+    node.type === NodeTypes.ELEMENT &&
+    node.tagType === ElementTypes.COMPONENT &&
+    node.codegenNode &&
+    node.codegenNode.type === NodeTypes.VNODE_CALL &&
+    node.codegenNode.children &&
+    !isArray(node.codegenNode.children) &&
+    node.codegenNode.children.type === NodeTypes.JS_OBJECT_EXPRESSION
+  ) {
+    node.codegenNode.children.properties.push(
+      createObjectProperty(
+        `__`,
+        createSimpleExpression(JSON.stringify(slotCacheKeys), false),
+      ) as SlotsObjectProperty,
+    )
+  }
+
   function getCacheExpression(value: JSChildNode): CacheExpression {
     const exp = context.cache(value)
     // #6978, #7138, #7114

+ 0 - 1
packages/compiler-core/src/transforms/vSlot.ts

@@ -342,7 +342,6 @@ export function buildSlots(
     : hasForwardedSlots(node.children)
       ? SlotFlags.FORWARDED
       : SlotFlags.STABLE
-
   let slots = createObjectExpression(
     slotsProperties.concat(
       createObjectProperty(

+ 6 - 1
packages/runtime-core/src/componentSlots.ts

@@ -75,6 +75,11 @@ export type RawSlots = {
    * @internal
    */
   _?: SlotFlags
+  /**
+   * cache indexes for slot content
+   * @internal
+   */
+  __?: number[]
 }
 
 const isInternalKey = (key: string) => key[0] === '_' || key === '$stable'
@@ -170,7 +175,7 @@ const assignSlots = (
     // when rendering the optimized slots by manually written render function,
     // do not copy the `slots._` compiler flag so that `renderSlot` creates
     // slot Fragment with BAIL patchFlag to force full updates
-    if (optimized || key !== '_') {
+    if (optimized || !isInternalKey(key)) {
       slots[key] = children[key]
     }
   }

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

@@ -2262,7 +2262,17 @@ function baseCreateRenderer(
       unregisterHMR(instance)
     }
 
-    const { bum, scope, job, subTree, um, m, a } = instance
+    const {
+      bum,
+      scope,
+      job,
+      subTree,
+      um,
+      m,
+      a,
+      parent,
+      slots: { __: slotCacheKeys },
+    } = instance
     invalidateMount(m)
     invalidateMount(a)
 
@@ -2271,6 +2281,13 @@ function baseCreateRenderer(
       invokeArrayFns(bum)
     }
 
+    // remove slots content from parent renderCache
+    if (parent && isArray(slotCacheKeys)) {
+      slotCacheKeys.forEach(v => {
+        parent.renderCache[v] = undefined
+      })
+    }
+
     if (
       __COMPAT__ &&
       isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)