Repository: vuejs/vue-next
File: packages/compiler-core/src/transforms/vSlot.ts (TypeScript)

Code before:

// Instead of being a DirectiveTransform, v-slot processing is called during
// transformElement to build the slots object for a component.
export function buildSlots(
  node: ElementNode,
  context: TransformContext,
  buildSlotFn: SlotFnBuilder = buildClientSlotFn
): {
  slots: SlotsExpression
  hasDynamicSlots: boolean
} {
  context.helper(WITH_CTX)

  const { children, loc } = node
  const slotsProperties: Property[] = []
  const dynamicSlots: (ConditionalExpression | CallExpression)[] = []

  // If the slot is inside a v-for or another v-slot, force it to be dynamic
  // since it likely uses a scope variable.
  let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0
  // with `prefixIdentifiers: true`, this can be further optimized to make
  // it dynamic only when the slot children actually uses the scope variables.
  if (!__BROWSER__ && !context.ssr && context.prefixIdentifiers) {
    hasDynamicSlots = children.some(child =>
      hasScopeRef(child, context.identifiers)
    )
  }

  // 1. Check for slot with slotProps on component itself.
  //    <Comp v-slot="{ prop }"/>
  const onComponentSlot = findDir(node, 'slot', true)
  if (onComponentSlot) {
    const { arg, exp } = onComponentSlot
    if (arg && !isStaticExp(arg)) {
      hasDynamicSlots = true
    }
    slotsProperties.push(
      createObjectProperty(
        arg || createSimpleExpression('default', true),
        buildSlotFn(exp, undefined, children, loc)
      )
    )
  }

  // 2. Iterate through children and check for template slots
  //    <template v-slot:foo="{ prop }">
  let hasTemplateSlots = false
  let hasNamedDefaultSlot = false
  const implicitDefaultChildren: TemplateChildNode[] = []
  const seenSlotNames = new Set<string>()
  let conditionalBranchIndex = 0

Review comment (suggestion):
I think this also needs to account for identifiers being used within the `v-slot` value itself, e.g.:

```html
<MyChild v-for="i in items" v-slot="{ value = i.value }">
```

- [Playground - This PR](https://deploy-preview-9427--vue-sfc-playground.netlify.app/#__DEV__eNqFUk1P4zAQ/SsjX8pKNBHb3UsVVgsVB5D4EHCrOYRk2hoc27KdUFTlvzO221AQgptn3syb55m3YSfGZF2LbMoKV1lhPDj0rfnHlWiMth42YLGsvOgQelhY3cCI6kcDfvk6WwlZb6Es38aBlIq4qrRyHoTHxsHxwHUw5wqIuytli1M4gv7wQ+L358Tkc+IPyQGuHn5xVeRJOommgCYZWXqkCKDYyevGC22PORMgVFLDGUzDIyTp3Y2d1J6C7QQSK7L06jmLZDR9B/Z9ZN/9Ns16bL3XCv5XUlTPgTaMmR89ENUg+29km61KtcQiTx3UXeR7stkh8472thDL7MlpRcfZhAGcVboxQqK9Nl7QXjmbQkQCVkqpXy5iztsW47Zizwqr5y/yT24dcpzdWHRoO+RswHxpl+gTfHZ3hWt6D2Cj61ZS9TfgLTot26AxlZ22qibZe3VR7Xm0kFDLe3e29qjc7lNBaKhMN+eMzDT75uvvcifZJPZx1dMW97z4hcFrXAiFN1YbdzAfhWONfnJTLbqtE84JCHYIbeQGGKd0EUwEearOU/nebVn/BnxtI+U=)
- [Playground - `main`](https://play.vuejs.org/#eNqFUk1P4zAQ/SsjX8pKNBELe6nCCqg4gMSHgFvNISTT1uDYlu2Eoir/nbHdhoIQ3DzzZt48z7w1OzUm61pkE1a4ygrjwaFvzX+uRGO09bAGi2XlRYfQw9zqBkZUPxrwq7fpUsh6A2X5Jg6kVMRVpZXzIDw2Do4Hrr0ZV0DcXSlbnMAB9PufEn+/Jg6/Jo5IDnD1+IerIk/SSTQFNMnI0iNFAMVWXjeea3vMmQChkhrOYBIeIUnvbuyk9hRsJpBYkaVXz1kko+lbsO8j+/a3adZT671WcFJJUb0E2jBmdvBIVIPsf5FtuizVAos8dVB3ke/IZvvMO9rbXCyyZ6cVHWcdBnBW6cYIifbGeEF75WwCEQlYKaV+vYw5b1uM24o9S6xevsk/u1XIcXZr0aHtkLMB86VdoE/w+f01rug9gI2uW0nVP4B36LRsg8ZUdtaqmmTv1EW1F9FCQi0e3PnKo3LbTwWhoTLdnDMy0/SHr3/IPcyOYh9XPW1xx4vfGLzGuVB4a7Vxe7NRONboNzfVots44YKAYIfQRm6AcUoXwUSQp+o8le/clvXvfQwj5g==)

Clicking the button should trigger an update in two places, but with the changes proposed here it no longer updates the value in the slot.

Code after:
export function buildSlots(
  node: ElementNode,
  context: TransformContext,
  buildSlotFn: SlotFnBuilder = buildClientSlotFn,
): {
  slots: SlotsExpression
  hasDynamicSlots: boolean
} {
  context.helper(WITH_CTX)

  const { children, loc } = node
  const slotsProperties: Property[] = []
  const dynamicSlots: (ConditionalExpression | CallExpression)[] = []

  // If the slot is inside a v-for or another v-slot, force it to be dynamic
  // since it likely uses a scope variable.
  let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0
  // with `prefixIdentifiers: true`, this can be further optimized to make
  // it dynamic when
  // 1. the slot arg or exp uses the scope variables.
  // 2. the slot children use the scope variables.
  if (!__BROWSER__ && !context.ssr && context.prefixIdentifiers) {
    hasDynamicSlots =
      node.props.some(
        prop =>
          isVSlot(prop) &&
          (hasScopeRef(prop.arg, context.identifiers) ||
            hasScopeRef(prop.exp, context.identifiers)),
      ) || children.some(child => hasScopeRef(child, context.identifiers))
  }

  // 1. Check for slot with slotProps on component itself.
  //    <Comp v-slot="{ prop }"/>
  const onComponentSlot = findDir(node, 'slot', true)
  if (onComponentSlot) {
    const { arg, exp } = onComponentSlot
    if (arg && !isStaticExp(arg)) {
      hasDynamicSlots = true
    }
    slotsProperties.push(
      createObjectProperty(
        arg || createSimpleExpression('default', true),
        buildSlotFn(exp, undefined, children, loc),
      ),
    )
  }

  // 2. Iterate through children and check for template slots
  //    <template v-slot:foo="{ prop }">
  let hasTemplateSlots = false
  let hasNamedDefaultSlot = false