]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-vapor): treat template v-for with single component child as component...
authoredison <daiwei521@126.com>
Wed, 24 Sep 2025 08:59:31 +0000 (16:59 +0800)
committerGitHub <noreply@github.com>
Wed, 24 Sep 2025 08:59:31 +0000 (16:59 +0800)
packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
packages/compiler-vapor/src/transforms/vFor.ts

index 69c695a246a424ef1a2ca66d473d418854fc4016..4b1574e5d2587b51d054d43aef30910bd0fb6be0 100644 (file)
@@ -235,6 +235,38 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`compiler: v-for > v-for on component 1`] = `
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
+const t0 = _template(" ")
+
+export function render(_ctx) {
+  const _component_Comp = _resolveComponent("Comp")
+  const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
+    const n3 = _createComponentWithFallback(_component_Comp)
+    const n2 = _child(n3)
+    _renderEffect(() => _setText(n2, _toDisplayString(_for_item0.value)))
+    return [n2, n3]
+  }, undefined, 2)
+  return n0
+}"
+`;
+
+exports[`compiler: v-for > v-for on template with single component child 1`] = `
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
+const t0 = _template(" ")
+
+export function render(_ctx) {
+  const _component_Comp = _resolveComponent("Comp")
+  const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
+    const n3 = _createComponentWithFallback(_component_Comp)
+    const n2 = _child(n3)
+    _renderEffect(() => _setText(n2, _toDisplayString(_for_item0.value)))
+    return [n2, n3]
+  }, undefined, 2)
+  return n0
+}"
+`;
+
 exports[`compiler: v-for > w/o value 1`] = `
 "import { createFor as _createFor, template as _template } from 'vue';
 const t0 = _template("<div>item</div>", true)
index 7357ad84fefafef4805bfbeb57054f4724d9b6db..f79bec89ddaed2501f6c6cf5807252d14c1bccaf 100644 (file)
@@ -368,4 +368,24 @@ describe('compiler: v-for', () => {
       index: undefined,
     })
   })
+
+  test('v-for on component', () => {
+    const { code, ir } = compileWithVFor(
+      `<Comp v-for="item in list">{{item}}</Comp>`,
+    )
+    expect(code).matchSnapshot()
+    expect(
+      (ir.block.dynamic.children[0].operation as ForIRNode).component,
+    ).toBe(true)
+  })
+
+  test('v-for on template with single component child', () => {
+    const { code, ir } = compileWithVFor(
+      `<template v-for="item in list"><Comp>{{item}}</Comp></template>`,
+    )
+    expect(code).matchSnapshot()
+    expect(
+      (ir.block.dynamic.children[0].operation as ForIRNode).component,
+    ).toBe(true)
+  })
 })
index d4cf42dbd55b9b9e3e23d8f44019cd901a475dd6..11acd6d221737435eb8a25d893faf6b78ac19d68 100644 (file)
@@ -2,6 +2,7 @@ import {
   type ElementNode,
   ElementTypes,
   ErrorCodes,
+  NodeTypes,
   type SimpleExpressionNode,
   createCompilerError,
 } from '@vue/compiler-dom'
@@ -28,7 +29,7 @@ export function processFor(
   node: ElementNode,
   dir: VaporDirectiveNode,
   context: TransformContext<ElementNode>,
-) {
+): (() => void) | undefined {
   if (!dir.exp) {
     context.options.onError(
       createCompilerError(ErrorCodes.X_V_FOR_NO_EXPRESSION, dir.loc),
@@ -47,7 +48,10 @@ export function processFor(
 
   const keyProp = findProp(node, 'key')
   const keyProperty = keyProp && propToExpression(keyProp)
-  const isComponent = node.tagType === ElementTypes.COMPONENT
+  const isComponent =
+    node.tagType === ElementTypes.COMPONENT ||
+    // template v-for with a single component child
+    isTemplateWithSingleComponent(node)
   context.node = node = wrapTemplate(node, ['for'])
   context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT
   const id = context.reference()
@@ -87,3 +91,16 @@ export function processFor(
     }
   }
 }
+
+function isTemplateWithSingleComponent(node: ElementNode): boolean {
+  if (node.tag !== 'template') return false
+
+  const nonCommentChildren = node.children.filter(
+    c => c.type !== NodeTypes.COMMENT,
+  )
+  return (
+    nonCommentChildren.length === 1 &&
+    nonCommentChildren[0].type === NodeTypes.ELEMENT &&
+    nonCommentChildren[0].tagType === ElementTypes.COMPONENT
+  )
+}