Skip to content

声明式 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 ComposeHarmonyOS ArkUI
语言KotlinArkTS
UI 声明Composable 函数@Component + build()
状态管理state hoisting@State/@Prop/@Link
动画animate*animateTo/transition
列表LazyColumn/RowLazyForEach
热重载
渲染引擎SkiaArkUI 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),不可变赋值触发刷新