先说一个我见过很多次的场景。
一个有五六年安卓经验的开发者,第一次写 ArkUI。他写完界面,想改一个文字,本能地去找那个 Text 控件对象——找了十分钟没找到,然后来问:「ArkUI 里怎么拿到控件?」
答案是拿不到。ArkUI 里根本不存在「控件对象」这个东西。
这就是声明式给传统开发者上的第一课,也是最难的一课。这篇把两种写法的本质分歧讲清楚,再给一条能落地的切换路径。
一、命令式:界面是「被操作」的
先看传统写法怎么工作。以安卓的 View 体系为代表:
// 传统命令式:先找到控件,再手动改它
TextView tv = findViewById(R.id.count_text);
Button btn = findViewById(R.id.add_btn);
btn.setOnClickListener(v -> {
count = count + 1;
tv.setText(String.valueOf(count)); // 手动把新数字写回界面
});
注意最后那行注释里的「手动」。数据变了,开发者要自己动手把界面改过来。 这是命令式的本质:界面不会自己动,你不推它它就不动。
业务简单时这没什么问题。一个计数器,手动改一行文字,很直观。
真实应用就不一样了。一个数据往往影响好几处界面:
购物车的商品数量变了——列表页的条目要变、底部总价的角标要变、详情页的「已加入」按钮状态要变、下单页的价格要变。四五处界面同步同一个数据,每处都得写更新代码。
漏改一处,「数据更新了界面没刷新」的经典 bug 就诞生了。
这类 bug 最难缠的地方在于它不报错、不崩溃。界面显示和真实数据对不上,测试报一个、用户再报三个,修完这处漏那处。做过客户端的人对这类 bug 都不陌生。
命令式写法下,这类 bug 的根源很明确:同步逻辑散落在几百个回调里,靠人肉保证一致,迟早漏。
二、ArkUI 声明式:界面是「算出来」的
ArkUI 把方向彻底反过来。同一个计数器,写法是这样:
@Entry
@Component
struct Counter {
@State count: number = 0; // 用装饰器标记:这个数据界面盯着
build() {
Column({
space: 20 }) {
Text(`${
this.count}`) // 只声明:这里显示 count 的值
.fontSize(48)
Button('加一')
.onClick(() => {
this.count++ }) // 只做一件事:改数据
}
.width('100%')
}
}
对比看两处:
- 没有「拿到 Text 对象改文字」这个动作,因为界面里根本没有可拿的对象。
- 按钮点下去只改
count,界面怎么变不需要你管。
count 一变,框架自动重算界面。开发者只负责声明关系(Text 显示 count)和维护数据(点击时 count++),中间那一步同步,整个交给框架。
三、本质差别只有一句话
抽象一下,两种写法的核心分歧只有一个问题:数据变了之后,界面靠什么保持正确?
| 命令式 | 声明式 | |
|---|---|---|
| 一致性由谁维护 | 开发者,记得改每一处 | 框架,统一重算 |
| 同步逻辑分布 | 散落在所有回调里 | 只有一份:你声明的「界面 = f(状态)」 |
| 会不会漏改 | 业务一复杂必然漏 | 不存在「漏改」这个概念 |
| 出问题的表现 | 界面与数据不一致,不报错 | 状态写错才会出错,报错更早 |
所以声明式不是「换一种写法装高级」。它是在架构层面消灭了一整类 bug——传统客户端最高发的「状态不一致」,在声明式框架里物理上不存在,因为没有需要手动改的地方。
代价也是真实的:
- 有传统经验的人要放下「操作控件」的旧本能;
- 零基础的人要接受「描述关系」这种更抽象的入门方式;
- 状态怎么划分、什么该是状态什么该是计算值,这些新概念要花时间焐热。
笔账算下来还是划算:学习成本一次性,收益是整个维护期的稳定性。
四、有传统经验的人,三步完成切换
按顺序练,大约一到两周。
第一步:戒掉「找控件」。
写 ArkUI 时你会本能地想「拿到那个 Text 改它的内容」。忍住。ArkUI 里拿不到任何控件对象,界面上只有「对数据的描述」。每次这种冲动冒出来,就提醒自己一句:别找控件,改数据去。
这一步的难点不在技术,在手感。写了五六年的习惯,头几天会反复犯。
第二步:把界面当函数理解。
声明式的世界观可以压成一个式子:界面 = f(状态)。
状态是唯一输入源,界面上看到的一切都是某个状态的投影。所以动手写界面前,先问自己两个问题:
- 这个页面有哪些状态?
- 每个状态影响界面的哪几个地方?
想清楚这两个,代码基本就对了。想不清楚就开始写,写出来的东西一定会在某个交互路径上不同步。
第三步:从单状态练到多状态。
顺序别跳:
- 先写只有一个状态的页面(计数器、开关切换)——确认自己真的理解「改状态界面自动变」;
- 再加到两三个状态(一个表单、一个带筛选的列表)——体验状态组合下的刷新;
- 最后写一个带列表、带详情、带筛选的小应用——这时候才真正遇到「状态该放哪一层」的问题。
练习量到了,转身自然完成。
顺带说个反直觉的现象:零基础的人在这件事上常常比老手快。 没有旧本能要戒,从第一天就按正确的方式写。老手要花两周克服的东西,新手可能没有。
五、排坑:新手最常踩的三个地方
这三个不是理论问题,是写代码时会真实卡住的地方。
坑一:改了数据界面没动。
最常见原因是状态没加装饰器。@State count: number = 0 里的 @State 漏了,它就是普通成员变量,改了框架不知道要重算。报错没有,界面就是不动,能卡很久。
坑二:改了对象的属性,界面没动。
@State 只监听引用变化,不监听对象内部字段的变化。this.user.name = 'x' 这样改,界面不会刷新;this.user = { ...this.user, name: 'x' } 才会。这个坑几乎人人都踩过一次,而且踩过之后会记一辈子。
坑三:不知道什么该放进状态。
有人把所有变量都标成 @State,结果页面刷新频繁、逻辑混乱;有人什么都不标,界面永远不动。判断标准其实简单:需要通过交互改变、且界面要跟着变的东西才是状态。反过来的「由其他状态算得出来的值」,不该单独存成状态,在 build 里算就行。
这三条对应的是「知道语法但不知道边界」——文档里每一条都能查到,但不会告诉你「你会在这里撞墙」。
六、写在最后
回到标题:为什么用声明式?
因为它把「数据与界面的一致性」从人肉维护交给框架,在架构层面消灭了传统客户端最常见的一类 bug。代价是放下旧本能、接受新的思维模型——用一到两周的练习,换整个维护期的稳定。
如果你只记一句,记这句:在 ArkUI 里,你不是在操作界面,你是在描述界面和状态的关系。
这一步迈过去,后面学多端适配、学状态管理,思路都会顺很多。迈不过去的人,会一直在找那个不存在的控件对象。
七、FAQ:几个被问得最多的问题
Q1:声明式是不是性能更差?
不一定。声明式框架并非每次都重建全部界面,它靠 diff 算出最小变更集再更新。反而因为跳过了大量手写的同步代码,很多场景下更快。真正影响性能的是状态划分是否合理——这也是为什么第五节第三个坑值得重视。
Q2:能混着写吗?会用到命令式的地方吗?
能。ArkUI 提供了直接操作组件的口子(比如通过 @Builder 之外的手段修改某些属性、或者调用组件的原生能力),只是不该成为主流写法。日常业务代码 95% 以上是声明式。
Q3:从 Vue 或 React 转过来是不是快一些?
快很多。它们都是声明式的,你已经有「状态驱动界面」的直觉,要适应的主要是装饰器语法和 ArkUI 的组件体系。反而是纯原生客户端背景的人最吃力——习惯最重。
Q4:@State 和 @Prop 有什么区别?
@State 是自己组件内部的状态源;@Prop 是从父组件传进来的单向副本,子组件改它不会影响父组件。多组件共享状态时还有 @Link(双向)、@Provide/@Consume(跨层级),选错会带来同步问题,这是学会 @State 之后下一个必须搞清的点。
Q5:必须先把 ArkTS 学透才能学 ArkUI 吗?
不需要学到「透」。能读懂类型声明、会写类和接口、知道不能动态加字段,这三样够了就可以开始。剩下的在写界面的过程中补,反而更快。
Q6:感觉鸿蒙学得太难,该怎么坚持?
学到一半想放弃,多数不是能力问题,是卡住的时间太长。环境报错卡一晚、状态不刷新查半天,连着来三次,谁都会怀疑自己。所以「坚持」不靠意志力硬扛,靠把卡住的时长压下去。
两件自己能做的:目标拆到当天能验收——不是「今天学 ArkUI」,是「今天把列表页的状态改到能刷新」,有验收点才知道有没有往前走;同一个问题卡过四十分钟就换路,去搜、去官方示例里找能跑的版本、去问人,磨到凌晨,消耗的是第二天。
还有一类卡点自己解决不了:你不知道自己错在哪。 第五节那三个坑就是典型,没有报错、界面就是不动,这时候缺的不是更多教程,是有人在旁边看一眼。
判断自己缺的正是这一块,报班补的就是它。别看「包教包会」,看两样:有没有强制的节奏、项目能不能上架。
拿誉天教育的 AI鸿蒙全栈就业班说:20 周、5 个月,含鸿蒙开发、多端全栈、AI 工程、智能体交互四块,脱产周一到周五全天,配每日和每周的测评。高频测评的实际意义在于——你落后两天就会被人发现,不用等到自己察觉「学不下去了」才回头。它的学生项目可以上架华为应用市场,这条比任何作品截图都硬:能过审上架,说明签名、提审、被驳回再改这一整套流程是真走完的。
问的时候两句话就够:测评多久一次、不合格怎么处理;往期项目在应用市场能不能搜到。