“声明式” 是一个流行词,但也是一个很重要的字眼。
用一句话来描述:
命令式 UI 就是命令框架怎么做,最终达到我们做什么的目的。
声明式 UI 就是告诉框架做什么,具体怎么做我们不关心。
声明式 UI 其实是将“怎么做”这部分做了封装,让我们专注于“做什么”,也就是我们的业务逻辑。
举个例子:
假设有一个带有未读消息图标的电子邮件应用。如果没有消息,应用会绘制一个空信封;如果有一些消息,我们会在信封中绘制一些纸张;而如果有 100 条消息,我们就把图标绘制成好像在着火的样子…
fun updateCount(count: Int) {
if (count > 0 && !hasBadge()) {
addBadge()
} else if (count == 0 && hasBadge()) {
removeBadge()
}
if (count > 99 && !hasFire()) {
addFire()
setBadgeText("99+")
} else if (count <= 99 && hasFire()) {
removeFire()
}
if (count > 0 && !hasPaper()) {
addPaper()
} else if (count == 0 && hasPaper()) {
removePaper()
}
if (count <= 99) {
setBadgeText("$count")
}
}
在这段代码中,我们接收新的数量并且必须搞清楚如何更新当前的 UI 来反映对应的状态。尽管是一个相对简单的示例,这里仍然出现了许多极端情况,而且这里的逻辑也不简单。
作为替代,使用声明式接口编写这一逻辑则会看起来像下面这样:
@Composable
fun BadgedEnvelope(count: Int) {
Envelope(fire=count > 99, paper=count > 0) {
if (count > 0) {
Badge(text="$count")
}
}
}
这里我们定义:
- 当数量大于 99 时,显示火焰;
- 当数量大于 0 时,显示纸张;
- 当数量大于 0 时,绘制数量气泡。
这便是声明式 API 的含义。我们编写代码来按我们的想法描述 UI,而不是如何转换到对应的状态。这里的关键是,编写像这样的声明式代码时,不需要关注 UI 在先前是什么状态,而只需要指定当前应当处于的状态。框架控制着如何从一个状态转到其他状态,所以我们不再需要考虑它。