]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-core): remove slot cache from parent renderCache during unmounting ...
authoredison <daiwei521@126.com>
Thu, 1 May 2025 09:55:36 +0000 (17:55 +0800)
committerGitHub <noreply@github.com>
Thu, 1 May 2025 09:55:36 +0000 (02:55 -0700)
* fix(compiler-core): remove slot cache from parent renderCache during unmounting

* chore: update

packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts
packages/compiler-core/src/transforms/cacheStatic.ts
packages/compiler-core/src/transforms/vSlot.ts
packages/runtime-core/src/componentSlots.ts
packages/runtime-core/src/renderer.ts

index ab5ed7baedeae9b77c95c8d00988b50e83188e16..358c0e31c3dc398c42cfff2a8655a1159c3fa7a7 100644 (file)
@@ -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]' },
+        },
       ],
     })
   })
index 8d5961643c1c36b05f86c5ab834e33383455aa66..e5d67380640a68327a5414caaed49effba2f97cd 100644 (file)
@@ -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
index db367f39c0c2a6adc9d3a7c3ac5f3c64e04a41f9..28625439a47e177d9e0ad972d8f306357bdf95d5 100644 (file)
@@ -342,7 +342,6 @@ export function buildSlots(
     : hasForwardedSlots(node.children)
       ? SlotFlags.FORWARDED
       : SlotFlags.STABLE
-
   let slots = createObjectExpression(
     slotsProperties.concat(
       createObjectProperty(
index 8f8392f1cdbb55326c946e5fbd66cf1d87038a37..44246add9b94033f640e3e257d5ff87276b26633 100644 (file)
@@ -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]
     }
   }
index a7f84dac7b41dac4421be1a52d77b48663aab6f4..651c77ec1ba9e52ddd941f3f111631d51406e4ed 100644 (file)
@@ -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)