---
title: "Composant VirtualList — zenit Zig UI"
description: "Ne monte que la fenêtre visible des grandes listes uniformes. item_count, item_height, overscan, la réutilisation et l'offset de défilement donnent un coût…"
url: https://zenit.z.express/fr/components/virtuallist
language: fr
alternate_en: https://zenit.z.express/components/virtuallist.md
alternate_zh: https://zenit.z.express/zh/components/virtuallist.md
alternate_es: https://zenit.z.express/es/components/virtuallist.md
alternate_ja: https://zenit.z.express/ja/components/virtuallist.md
alternate_ko: https://zenit.z.express/ko/components/virtuallist.md
alternate_de: https://zenit.z.express/de/components/virtuallist.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# VirtualList

`ui.widgets.VirtualList`

Ne monte que la fenêtre visible des grandes listes uniformes.

[Video](https://zenit.z.express/media/stories/virtuallist.mp4?v=08128b8ded)

**BEHAVIOR CONTRACT**

item\_count, item\_height, overscan, la réutilisation et l'offset de défilement donnent un coût stable en O(visible).

**RECORDED INTERACTION**

Le harness fait défiler ; les nœuds ne couvrent que la plage visible.

`examples/storybook/stories.zig:2944`

```zig
pub fn buildVirtualList(scope: *ui.Scope, cx: *ui.Cx) anyerror!*ui.Node {
    const vl = try W.VirtualList(.{ .item_count = 1000, .item_height = 28, .width = 320, .height = 360 }).mount(scope, cx, vlRender);
    return vl.container;
}
```

[Voir le code sur GitHubexamples/storybook/stories.zig:2944](https://github.com/version-next/zenit/blob/HEAD/examples/storybook/stories.zig#L2944)

Le code source tel quel de la story dans le Storybook (zenit 5f9add5+wip 2026-09-30). `W` désigne `ui.widgets` ; `col` / `row` / `label` sont de petits helpers de mise en page du Storybook.

### VirtualListProps

`ui.widgets.VirtualList`

[src/ui/components/virtual\_list/mod.zig:50](https://github.com/version-next/zenit/blob/HEAD/src/ui/components/virtual_list/mod.zig#L50)

```zig
fn VirtualList(props: VirtualListProps) VirtualListBuilder
```

| Champ | Type | Défaut | Description |
| --- | --- | --- | --- |
| `item_count` | `usize` | `0` | 总 item 数量 |
| `item_height` | `f32` | `32` | 每个 item 的固定高度（px） |
| `width` | `?f32` | `null` | 容器宽度 |
| `height` | `?f32` | `null` | 容器高度 |
| `overscan` | `usize` | `5` | 视口外额外渲染的行数（上下各 overscan 行） |
| `padding` | `Padding` | `Padding.ZERO` | 内边距 |
| `background` | `Color` | `Color.TRANSPARENT` | 容器背景色 |
| `scroll_speed` | `f32` | `1` | 滚动速度乘数 |
| `scroll_direction` | `ScrollDirection` `.vertical` `.horizontal` `.both` | `.vertical` | 滚动方向（默认纵向） |
| `content_width` | `?f32` | `null` | 内容宽度（用于横向滚动，null=由布局自动决定） |
| `item_height_fn` | `?*const fn (index: usize, user_context: ?*anyopaque) f32` | `null` | 可选：按 item 返回高度（px）。给了它就进入\*\*不等高模式\*\*。 null（默认）→ 所有 item 用 \`item\_height\`，与以前完全一致。 非 null → 总高、可见范围、spacer、ensureVisible 全部改走前缀和。 回调必须是纯函数且对同一 index 稳定：它每帧会被调用若干次，返回值 若在同一帧内变化，滚动几何会自相矛盾。 |
| `item_height_context` | `?*anyopaque` | `null` | 传给 \`item\_height\_fn\` 的上下文（通常与 render 的 user\_context 同一个）。 |
| `measure_items` | `bool` | `false` | \*\*动态测量模式\*\*：行高由内容自己决定，组件量出来再记账。 三种高度模式的取舍： - 默认（都不设）→ 等高，\`item\_height\`，O(1) 定位，最快。 - \`item\_height\_fn\` → 不等高但\*\*高度可预先算出\*\*（如 diff 行：代码行 20、 hunk 头 28）。不需要测量，几何一次到位。 - \`measure\_items = true\` → 不等高且\*\*高度事先不知道\*\*（如自动换行的 评论、聊天气泡）。行先按 \`estimate\_item\_height\` 占位，布局后读回真实 高度并回填，同时补偿滚动位置。 同时设了 \`item\_height\_fn\` 时以 \`item\_height\_fn\` 为准（它更便宜且精确）。 |
| `estimate_item_height` | `f32` | `32` | 未测量行的占位高度。估得越准，滚动条抖动越小。 |
| `item_key_fn` | `?*const fn (index: usize, user_context: ?*anyopaque) measurements.ItemKey` | `null` | 可选：index → 稳定 key。给了它，实测高度就跟着\*\*数据\*\*走而不是跟着 位置走 —— 在头部插入一条时，后面所有行的实测值依然有效。 null = 用 index 本身（头部增删会让后续行重新测量）。 |
| `item_key_context` | `?*anyopaque` | `null` | 传给 \`item\_key\_fn\` 的上下文（null 时复用 render 的 user\_context）。 |

### MountResult

`ui.widgets.VirtualList`

mount renvoie directement `MountResult` : ajoutez-le à un parent.
