You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qwik中如何循环渲染<Slot />?动态场景下name属性处理

Qwik循环渲染Slot内容仅显示一次的解决方法

问题原因

你遇到的问题本质是Qwik的匿名Slot消费机制:匿名<Slot />是「一次性消费」的,所有子元素只会被插入到DOM一次。当你在循环中重复使用<Slot />时,只有第一个Slot会渲染全部子元素,后续的Slot因为没有剩余子元素可消费,会呈现为空,导致看起来只渲染了第一组内容。

Qwik文档提到的「多个Slot需不同name」是针对命名Slot的场景,用于区分不同的子内容区域,但不适用于你这种「重复渲染同一批子内容」的动态循环场景。

解决方案:使用useSlot()捕获内容后重复渲染

要实现动态次数的Slot内容复用,你需要先通过useSlot()钩子捕获Slot的完整内容,再在循环中重复渲染这个内容片段。

修改后的marquee.tsx代码

import { cn } from '@/lib/utils'
import {
  Slot,
  component$,
  useVisibleTask$,
  useSlot,
  Fragment,
  type HTMLAttributes,
} from '@builder.io/qwik'

export type MarqueeProps = HTMLAttributes<HTMLDivElement> & {
  direction?: 'left' | 'up'
  pauseOnHover?: boolean
  reverse?: boolean
  fade?: boolean
  innerClassName?: string
  numberOfCopies?: number
}

export const Marquee = component$<MarqueeProps>(
  ({
    direction = 'left',
    pauseOnHover = false,
    reverse = false,
    fade = false,
    innerClassName,
    numberOfCopies = 5,
    ...props
  }) => {
    useVisibleTask$(() => {
      console.log({ numberOfCopies }) // 5
    })
    // 捕获Slot的完整内容
    const slotContent = useSlot()

    return (
      <div
        class={cn(
          'group flex gap-[1rem] overflow-hidden',
          direction === 'left' ? 'flex-row' : 'flex-col',
          props.class,
        )}
        style={{
          maskImage: fade
            ? `linear-gradient(${
                direction === 'left' ? 'to right' : 'to bottom'
              }, transparent 0%, rgba(0, 0, 0, 1.0) 10%, rgba(0, 0, 0, 1.0) 90%, transparent 100%)`
            : undefined,
          WebkitMaskImage: fade
            ? `linear-gradient(${
                direction === 'left' ? 'to right' : 'to bottom'
              }, transparent 0%, rgba(0, 0, 0, 1.0) 10%, rgba(0, 0, 0, 1.0) 90%, transparent 100%)`
            : undefined,
        }}
        {...props}>
        {Array(numberOfCopies)
          .fill(0)
          .map((_, i) => (
            <div
              key={i}
              class={cn(
                'flex shrink-0 justify-around gap-[1rem] [--gap:1rem]',
                direction === 'left'
                  ? 'animate-marquee-left flex-row'
                  : 'animate-marquee-up flex-col',
                pauseOnHover && 'group-hover:[animation-play-state:paused]',
                reverse && 'direction-reverse',
                innerClassName,
              )}>
              {/* 重复渲染捕获到的Slot内容 */}
              <Fragment>{slotContent}</Fragment>
            </div>
          ))}
      </div>
    )
  },
)

关键改动说明

  1. 导入useSlot和Fragment:useSlot()用于获取Slot的所有子元素内容,Fragment用于包裹内容片段避免额外DOM节点。
  2. 捕获Slot内容:在组件逻辑中调用const slotContent = useSlot(),得到Slot子元素的数组引用。
  3. 循环渲染内容:将原有的<Slot />替换为<Fragment>{slotContent}</Fragment>,这样每个循环项都会完整渲染Slot的全部子内容。

注意事项

  • 如果Slot内包含带状态的Qwik组件,重复渲染会创建多个独立的组件实例,每个实例拥有自己的状态,这通常符合跑马灯组件的预期。
  • 确保numberOfCopies是合理的数值,避免过多重复导致性能问题。

内容的提问来源于stack exchange,提问作者zougari47

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:27:34