Appearance
声明式 UI 范式
ArkUI 的核心:声明式 UI = "描述 UI 应该是什么样子",而不是"一步步怎么画"。
1. 声明式 vs 命令式
命令式 UI(传统 Android XML + Java/Kotlin)
kotlin
// Android:命令式,告诉系统"怎么画"
val textView = TextView(context)
textView.text = "Hello"
textView.setTextColor(Color.BLUE)
textView.setTextSize(24f)
linearLayout.addView(textView)
// 数据变了 → 重新执行一遍上面的代码声明式 UI(ArkTS ArkUI)
typescript
// 鸿蒙:声明式,告诉系统"UI 应该长什么样"
Text("Hello")
.fontSize(24)
.fontColor(Color.Blue)
// 数据变了 → 框架自动重新渲染2. 声明式 UI 的核心原理
状态(@State)变化
↓
框架检测变化
↓
重新执行 build()
↓
对比新旧 UI 树(Diff)
↓
只更新变化的节点
↓
UI 刷新关键特性
| 特性 | 说明 |
|---|---|
| UI 是状态的函数 | UI = f(state),状态确定 UI |
| 单向数据流 | 数据 → UI,不是 UI → 数据 |
| 不可变性 | 通过创建新对象触发 UI 更新 |
| Diff 算法 | 框架自动对比新旧树,只更新变化 |
3. 声明式 UI 的优势
3.1 代码简洁
typescript
// 命令式(Android):20+ 行
Button btn = new Button(context)
btn.setText("提交")
btn.setBackgroundColor(Color.BLUE)
btn.setTextColor(Color.WHITE)
btn.setTextSize(16)
btn.setWidth(200)
btn.setHeight(50)
btn.setGravity(Gravity.CENTER)
btn.setOnClickListener(v -> {})
// 声明式(ArkTS):5 行
Button("提交")
.backgroundColor(Color.Blue)
.fontColor(Color.White)
.fontSize(16)
.width(200)
.height(50)3.2 可预测性
typescript
// 给定相同的状态,UI 一定相同(纯函数特性)
// state = { count: 0 } → Text("Count: 0")
// state = { count: 1 } → Text("Count: 1")
// 没有副作用,没有随机性3.3 热重载(Hot Reload)
修改代码 → DevEco Studio 热加载 → 实时查看效果
(无需重新编译安装,开发效率大幅提升)4. 声明式 UI 的核心概念
4.1 组件(Component)
typescript
// 每个 UI 元素都是一个组件
Column() // 布局容器
Text("标题") // 文本
Button("按钮") // 按钮
Image("icon") // 图片4.2 属性链(Attribute Chain)
typescript
Text("Hello")
.fontSize(24) // 链式属性
.fontColor(Color.Blue) // 继续链
.fontWeight(FontWeight.Bold)
.margin({ top: 10 })4.3 不可变数据触发 UI 更新
typescript
@Component
struct Counter {
@State count: number = 0
build() {
Column() {
Text(`计数: ${this.count}`)
.fontSize(32)
.margin({ bottom: 20 })
Button('增加')
.onClick(() => {
// 修改 @State 触发 UI 更新
this.count = this.count + 1 // 不可变赋值
})
}
}
}5. 与 Android Compose 的对比
| 维度 | Android Compose | HarmonyOS ArkUI |
|---|---|---|
| 语言 | Kotlin | ArkTS |
| UI 声明 | Composable 函数 | @Component + build() |
| 状态管理 | state hoisting | @State/@Prop/@Link |
| 动画 | animate* | animateTo/transition |
| 列表 | LazyColumn/Row | LazyForEach |
| 热重载 | ✅ | ✅ |
| 渲染引擎 | Skia | ArkUI Render Service |
6. 声明式 UI 的局限性
6.1 学习曲线
命令式 UI 开发者 → 声明式 UI
├─ 需要转变思维:从"怎么做"到"是什么"
├─ 理解不可变性触发更新
├─ 掌握状态管理机制
└─ 习惯纯函数的 UI 描述6.2 调试难度
typescript
// 声明式 UI 调试的痛点
// 问题:UI 没按预期更新
// 原因可能:
// 1. @State 没检测到变化(对象引用没变)
// 2. 在 build() 中做了非 UI 操作
// 3. 数据流动路径错误
// 4. 状态作用域不对6.3 复杂动画限制
typescript
// 声明式动画适合简单场景
// 复杂动画可能需要 XComponent + OpenGL
Button('动画')
.animation({
duration: 300,
curve: Curve.EaseInOut
})
.scale({ x: 1.2, y: 1.2 })7. 面试高频考点
Q1: 什么是声明式 UI?和命令式 UI 的区别?
回答:声明式 UI 是"描述 UI 应该长什么样",UI = f(state),状态变化时框架自动重新渲染。命令式 UI 是"一步步怎么画",需要手动更新每个元素。声明式更简洁、可预测。
Q2: 为什么不可变数据能触发 UI 更新?
回答:@State 装饰的状态变量被 ArkUI 代理拦截,赋值新对象时触发标记脏节点,框架 Diff 后只更新变化的部分。
Q3: 声明式 UI 有什么局限性?
回答:思维转变(从命令式到声明式)、调试难度(原因可能多层)、复杂动画受限(可能需要 OpenGL)。
🐱 小猫提示:声明式 UI 是鸿蒙开发的第一课,也是面试必考题。记住核心:UI = f(state),不可变赋值触发刷新。