所有 Kuikly 页面都由这三个概念组合而成,理解它们 = 理解 80% 的 Kuikly。
页面基类,整张屏幕的根。持有状态、生命周期、UI 描述。
带 receiver 的 Kotlin 闭包,描述 UI 长什么样、组件怎么嵌套。
每个组件的样式 + 交互配置块。attr 写"长啥样",event 写"响应啥"。
@Page("MinimalPage")
class MinimalPage : Pager() { // ← Pager 基类
private var count by observable(0) // ← 状态
override fun body(): ViewBuilder { // ← ViewBuilder
val ctx = this
return {
View {
attr { // ← attr:样式
allCenter()
backgroundColor(Color.WHITE)
}
event { // ← event:交互
click { ctx.count++ }
}
Text { attr { text("${ctx.count}") } }
}
}
}
}
按时间顺序触发,每个阶段适合做不同的事情:
读 pageData、初始化字段、订阅长期数据流。UI 还没渲染,不能操作 view。
引擎拿到 ViewBuilder,构建 BuildTree → RenderTree → 下发原生指令。后续 attr 字段变化也会重跑 attr 块。
发 PV 埋点、起入场动画、启动定时器、申请权限。
用户点击、滑动、网络回调 → 改 observable → attr 块重跑 → UI 自动更新。
取消所有正在进行的网络请求、解绑监听、停止定时器,避免内存泄漏。
| 做了什么 | 放错了哪 | 正确位置 |
|---|---|---|
| 调 pageData.params | 放 body() 里 | created() |
| 发埋点 | 放 created() 里(页面还没显示) | pageDidAppear() |
| 取消 timer | 没写(页面销毁后还在跑) | pageWillDestroy() |
| fetch user info | 写在 attr {} 里 | created() 或 pageDidAppear() |
下面这段 DSL 代码:
View {
attr { allCenter() }
Text { attr { text("Hello") } }
Image { attr { src("avatar.png") } }
View {
attr { flexDirectionRow() }
Text { attr { text("A") } }
Text { attr { text("B") } }
}
}
组件名 { attr{} event{} 子组件 }每个组件后面都有这两个块,分得很清楚 —— attr 写"是什么",event 写"做什么"。
| 原因 | 解释 |
|---|---|
| 职责清晰 | "长啥样"和"响应啥"逻辑分离,DSL 一眼能扫 |
| 响应式追踪精度 | attr 单独成块,引擎能精确知道哪些 observable 被它读了,更新只刷该属性 |
| 渲染指令拆分 | attr → setProp 指令;event → addEventListener 指令;分开方便批量合并 |
| 执行频率不同 | attr 会被多次调用(每次 observable 变化);event 只在挂载时注册一次 |
// ❌ 错:attr 块会被多次执行
attr {
text("${ctx.count}")
println("rendering") // ← 重复打印
fetchUser() // ← 重复请求
}
// ✅ 对:副作用放生命周期或 event
override fun pageDidAppear() {
fetchUser()
}
调整下面的下拉菜单,看看 flex 各属性如何影响布局:
View {
attr {
flexDirectionRow()
justifyContentFlexStart()
alignItemsStretch()
flexWrapNowrap()
}
Text { attr { text("A") } }
Text { attr { text("B") } }
Text { attr { text("C") } }
Text { attr { text("D") } }
}
| 需求 | Kuikly API |
|---|---|
| 横向排列 | flexDirectionRow() |
| 子组件全部居中 | allCenter() |
| 两端对齐 / 中间均分 | justifyContentSpaceBetween() |
| 子组件占满剩余空间 | 子组件 attr { flex(1f) } |
| 换行排列 | flexWrapWrap() |
这是 3.7 节代码的浏览器渲染版。点击红心 / 关注按钮观察响应式 UI 更新效果。
View { // 卡片容器
attr { backgroundColor(Color.WHITE); borderRadius(12f); padding(16f) }
View { // 上半部分:头像 + 名字
attr { flexDirectionRow(); alignItemsCenter() }
Image { attr { src(...); size(48f, 48f); borderRadius(24f) } }
View {
attr { marginLeft(12f); flex(1f) }
Text { attr { text("张三"); fontSize(16f); fontWeightBold() } }
Text { attr { text("Kuikly 学习中"); fontSize(12f); marginTop(4f) } }
}
}
View { // 下半部分:操作按钮
attr { flexDirectionRow(); marginTop(16f); justifyContentSpaceBetween() }
View { // 点赞按钮(响应式)
attr {
paddingHorizontal(16f); height(36f); borderRadius(18f)
backgroundColor(if (ctx.liked) Color(0xFFFFEBEEL) else Color(0xFFF5F5F7L))
}
event { click { ctx.liked = !ctx.liked; ctx.likeCount += if (ctx.liked) 1 else -1 } }
Text { attr { text(if (ctx.liked) "♥" else "♡") } }
Text { attr { text("${ctx.likeCount}") } }
}
View { // 关注按钮
attr { allCenter(); backgroundColor(Color(0xFF1976D2L)); height(36f) }
event { click { /* TODO 关注 */ } }
Text { attr { text("+ 关注"); color(Color.WHITE) } }
}
}
}