为什么你的 MVI 页面代码读起来比本该有的难度更高------以及一个简单的解决办法
如果你从 MVVM 迁移到了 MVI,大概率遇到过这种情况:团队盯着复杂页面的代码,绞尽脑汁也理不清流程。架构本身"没问题",但总感觉哪里不对劲。
问题根源何在?糟糕的命名规范------具体来说,是把 UI 回调和 MVI 意图(Intent)混为一谈。
让我具体说明。
MVVM 遗留习惯
在 MVVM 中,我们不会过多思考 UI 触发操作该如何转化为行为,只是直接调用 ViewModel 的方法:
c
// MVVM - 简单直接
Button(onClick = { viewModel.login() })
TextField(onValueChange = { viewModel.updateEmail(it) })
这种方式有效且直观,但当切换到 MVI 时,这个习惯就会引发问题。
MVI 的不同之处
MVI 构建的核心是 语义清晰性 。每一个意图(Intent)都代表某件已经发生的事,是一个在单向数据流管道中传递的事件。架构本身就承载着明确的语义。
如果你模糊了"监听某件事"和"某件事已发生"的界限,就会破坏这种清晰性。
两种场景,两种规范
这个思维模型能拯救你的代码库:
1. UI 回调 → 现在时 + "On" 前缀
这些是触发器,你正在为未来可能发生的事件注册监听器。
c
// ✅ 正确示例 - 监听触发事件
Button(onClick = { onLoginClick() })
TextField(onValueChange = { onEmailChange(it) })
可以这样理解:"当这件事发生时,执行对应的操作。"
这里用 Clicked 是错误的,因为事件尚未发生,你只是在设置监听器。
2. MVI 意图(Intent)→ 过去分词
这些是已经发生的事件,现在要发送到你的状态处理器(reducer/ViewModel) 中。
c
// ✅ 正确示例 - 事件已发生
sealed interface LoginIntent {
data object LoginButtonClicked : LoginIntent
data class EmailChanged(val value: String) : LoginIntent
data class PasswordChanged(val value: String) : LoginIntent
data object ForgotPasswordTapped : LoginIntent
}
可以这样理解:"这件事已经发生了,需要处理。"
这里用 OnLoginClick 是多余的,因为意图(Intent)本身就定义为"已发生的事件"。
完整示例
规范的 MVI 代码应该是这样的:
c
@Composable
fun LoginScreen(
state: LoginState,
onIntent: (LoginIntent) -> Unit
) {
TextField(
value = state.email,
onValueChange = { onIntent(EmailChanged(it)) } // 回调 → 意图
)
Button(
onClick = { onIntent(LoginButtonClicked) } // 回调 → 意图
) {
Text("Login")
}
}
注意其中的转化逻辑:
**onValueChange**(现在时,触发器)→**EmailChanged**(过去时,事件)**onClick**(现在时,触发器)→**LoginButtonClicked**(过去时,事件)
MVVM 到 MVI 的思维转变
| 维度 | MVVM | MVI |
|---|---|---|
| UI 触发方式 | 直接调用方法 | 发送意图(Intent) |
| 命名核心 | 要执行什么操作 | 发生了什么事 |
| 流程方向 | 双向感知 | 单向、显式 |
| 语义权重 | 低 | 高 |
在 MVVM 中,viewModel.login() 是一条"指令";而在 MVI 中,LoginButtonClicked 是一个"事实"。
为什么这很重要
当你的团队阅读遵循规范命名的 MVI 代码时:
- 意图自文档化:能清晰知道每个状态变化的触发源
- 调试更简单:日志读起来像"故事线":"EmailChanged → PasswordChanged → LoginButtonClicked → LoginFailed"
- 新人上手更快:无需额外文档就能理解流程
速查指南
| 场景 | 命名规范 | 示例 |
|---|---|---|
| UI 回调 | on + 现在时 |
onClick、onTextChange |
| MVI 意图 | 过去分词 | Clicked、Changed、Selected |
核心结论
MVI 的优势在于其显式性。不要因为沿用 MVVM 的不良命名习惯,而削弱它的优势。
UI 回调是"承诺",意图是"事实"。
按照这个原则命名,你会发现复杂页面的代码重新变得清晰易读。