| 需求 | 语法 | 例子 |
|---|---|---|
| 条件渲染(vif 风格) | if / when / ?.let | if (loggedIn) Text { } else Button { } |
| 短列表(<20 条) | forEach | cities.forEach { Text { ... } } |
| 长列表 / 滚动 | List + vfor | List { vfor({ items }) { ... } } |
| 整页翻动 | PageList + vfor | 抖音上下滑、轮播图 |
| 瀑布流 | WaterfallList + vfor | 小红书首页、Pinterest |
| 维度 | forEach | vfor |
|---|---|---|
| 调用时机 | attr 块执行时(一次) | List 滚动时按需 |
| 创建节点 | 全部 | 仅可视区附近 |
| 复用 | ❌ | ✅ 滑出回收,滑入复用 |
| 内存 | 跟数据量线性 | 跟可视区相关 |
| 用在哪 | 普通容器(View / Scroller) | List / PageList / WaterfallList |
// ❌ 1000 条数据用 Scroller + forEach
Scroller {
items.forEach { Text { ... } } // 创建 1000 个 View → 内存 ~50MB+,首屏卡 1-2s
}
// ✅ 用 List + vfor
List {
vfor({ items }) { Text { ... } } // 实际只创建 ~12 个 View → 内存 ~5MB
下面两个"模拟器"都展示同一份 100 条数据。左边模拟 Scroller(无复用),右边模拟 List(有复用)。 右上角统计:实际创建的 View 数。滚动右边的 List 模拟器,注意"已复用"次数会增加。
屏幕高度 200px,每个 item 50px → 一屏 4 个 item
List 实际策略(橙色 = 被复用的 View):
┌─────────────────────────────────────────────┐
│ View1 ← 显示 item[0] (绿色:原创建) │
│ View2 ← 显示 item[1] │
│ View3 ← 显示 item[2] │
│ View4 ← 显示 item[3] │
│ View5 ← 缓冲区 (即将进入) │
└─────────────────────────────────────────────┘
向下滚 → item[0] 滑出 → View1 进入回收池 →
显示 item[5]:从池中取 View1,更新数据为 item[5] (橙色:复用)
最终:View 实例总数永远 ≈ 一屏数 + 缓冲区
常见的「加载中 / 空数据 / 失败 / 成功」4 态切换,用 when 一气呵成。
when (ctx.status) {
"loading" -> View { Text { attr { text("⏳ 加载中…") } } }
"empty" -> View { Text { attr { text("📭 暂无数据") } } }
"error" -> View {
Text { attr { text("加载失败"); color(Color.RED) } }
View {
event { click { ctx.retry() } }
Text { attr { text("点击重试") } }
}
}
"success" -> List { vfor({ ctx.items }) { ... } }
}
List + vfor + 头像 + 未读红点的标准实现。点击消息行模拟跳转。
List {
attr { flex(1f); paddingTop(8f) }
event {
scrollEnd { e ->
if (atBottom(e) && !ctx.loadingMore) ctx.loadMore()
}
}
Refresh {
attr { refreshing(ctx.refreshing) }
event { refresh { ctx.refresh() } }
}
vfor({ ctx.messages }) { msg ->
renderMessageRow(msg).invoke(this)
}
if (ctx.loadingMore) {
View { Text { attr { text("加载更多…") } } }
}
}
WaterfallList + 高度可变的 cover 图 → 经典瀑布效果。
WaterfallList {
attr {
flex(1f)
columnCount(2) // 2 列
columnSpacing(8f)
rowSpacing(8f)
}
vfor({ ctx.notes }) { note ->
View {
attr { backgroundColor(Color.WHITE); borderRadius(8f) }
Image {
attr {
src(note.coverUrl)
width(100f)
height(note.coverHeight) // ★ 关键:高度可变
}
}
Text { attr { text(note.title) } }
}
}
}