要点
鸿蒙入门到日常开发的名词条目分七组:状态装饰器、UI 组件、路由、生命周期、并发、网络与权限、报错速查。每条给签名、一句话定位、最小示例和坑;字典是查的,文末速查表做全量索引。语言层的”为什么”不在此展开,见系列第三篇。
状态管理装饰器(V1 / V2)
V1(@Component 系)与 V2(@ComponentV2 系)两套观测机制并存、不可混用;V1 观测第一层,V2 到属性级,工程内保持一致。
@State(V1)
@Component
struct Counter {
@State count: number = 0 // 必须本地初始化
build() {
Button(`${this.count}`).onClick(() => { this.count += 1 })
}
}
组件内部状态的默认选择,变化即刷新本组件。
⚠️ 坑:只观测第一层。this.list.push(x) 能刷新,this.list[0].done = true 不能。
@Prop / @Link(V1)
@Component
struct Child {
@Prop title: string = '' // 单向拷贝:父变我变,我变只影响自己
@Link liked: boolean // 双向:修改同步回父
build() {
Column({ space: 8 }) {
Text(this.title)
Toggle({ isOn: this.liked })
.onChange((v: boolean) => { this.liked = v })
}
}
}
// 父组件调用:Child({ title: this.msg, liked: $isLiked })
@Prop 收快照、@Link 收引用,分别对应”只读展示”与”子组件可回写”。
⚠️ 坑:@Link 传值忘写 $ 前缀直接编译报错;@Prop 是深拷贝,传大对象有性能代价。
@Provide / @Consume(V1)
// 顶层:@Provide theme: string = 'dark'
// 任意深层后代:@Consume theme: string 按变量名匹配,免去逐层透传
跨层级共享,命名即通道。
⚠️ 坑:同名即配对,两个顶层 @Provide 同名变量时后代拿到谁不可预期。
@Observed + @ObjectLink(V1)
@Observed
class Task {
done: boolean = false
}
@Component
struct TaskRow {
@ObjectLink task: Task // 不能本地初始化,必须由父传入
build() {
Text(this.task.done ? '完成' : '进行中')
.onClick(() => { this.task.done = !this.task.done }) // 类内属性变化可观测
}
}
V1 时代补嵌套观测的组合拳:可观察类 + 接收子组件。
⚠️ 坑:只观测该实例自身的属性,属性的属性(再嵌套一层)仍看不见。
@ComponentV2 + @Local / @Param / @Event(V2)
@ComponentV2
struct PriceCard {
@Local selected: boolean = false // 取代 @State
@Param price: number = 0 // 外部只读传入
@Event onBuy: () => void = () => {} // 子到父的事件回调
build() {
Text(`¥${this.price}`)
.onClick(() => { this.selected = true; this.onBuy() })
}
}
V2 组件三件套:本地状态、只读入参、显式回调,双向绑定拆成下行数据 + 上行事件。
⚠️ 坑:在 @ComponentV2 里写 @State、在 @Component 里写 @Local,都直接报”装饰器不适用”。
@ObservedV2 + @Trace(V2)
@ObservedV2
class Cart {
@Trace items: string[] = [] // 属性级观测:第 N 层变化都刷新
@Trace total: number = 0
note: string = '' // 未标 @Trace:怎么改都不触发 UI
}
属性级深度观测,嵌套刷新问题的终点。
⚠️ 坑:类里漏标 @Trace 的属性是观测盲区,编译器不会替你检查”忘了标”。
@Monitor(V2)
@ComponentV2
struct Page {
@Local page: number = 1
@Monitor('page') // 监听状态变量名
onPageChange() { // 回调可带 IMonitor 参数取变化前后值
console.info('翻页了')
}
}
声明式的”watch”,替代在 onClick 里手写副作用。
UI 组件
组内共性:属性全部链式调用,没有 CSS;布局组件自身就是 flex 语义。
Text / Button / TextInput
Text('标题').fontSize(20).fontWeight(FontWeight.Bold)
Button('提交', { type: ButtonType.Capsule }).width('50%')
TextInput({ placeholder: '搜索' })
.onChange((v: string) => { /* v 即最新输入 */ })
三大基础件;Button 内容也可换成子组件。
⚠️ 坑:TextInput 的值由状态驱动时,onChange 里要同步写回状态,否则输入框”自己显示”与状态脱节。
Column / Row / Stack
Column({ space: 12 }) { /* 垂直排 */ }
Row({ space: 8 }) { /* 水平排 */ }
Stack({ alignContent: Alignment.BottomEnd }) { /* 叠放,后者盖前者 */ }
布局三板斧,space 是子项间距,对齐用 justifyContent / alignItems 两族属性。
⚠️ 坑:Row 里想让某项占满剩余宽度用 layoutWeight(1),不存在 flex-grow 这个属性名。
List + ForEach
List({ space: 8 }) {
ForEach(this.todos, (item: Todo) => {
ListItem() { Text(item.title) }
}, (item: Todo) => item.id.toString()) // key 生成器
}.layoutWeight(1)
滚动列表的标准写法,ListItem 是直接子节点。
⚠️ 坑:key 用下标,删除中间项即错位;key 必须稳定且唯一。
Grid
Grid() {
GridItem() { Text('1') }
GridItem() { Text('2') }
}
.columnsTemplate('1fr 1fr') // 两列等宽;fr 单位表达比例
网格容器,配 GridItem 使用;响应式栅格另有 GridRow/GridCol 一套。
⚠️ 坑:不设 columnsTemplate 时接近单列,忘了配是最常见的”没成网格”。
路由
官方推荐 Navigation(组件式导航);@ohos.router(页面级路由)停止演进、存量仍在用。两条路线并存,新项目选 Navigation。
Navigation + NavDestination
@Component
struct Home {
pathStack: NavPathStack = new NavPathStack()
build() {
Navigation(this.pathStack) {
Button('详情').onClick(() => {
this.pathStack.pushPathByName('detail', 42) // 压栈并传参
})
}
.navDestination(this.pageMap)
}
@Builder pageMap(name: string) {
if (name === 'detail') { Detail() }
}
}
@Component
struct Detail {
build() {
NavDestination() { Text('详情页') }
}
}
页面栈由 NavPathStack 持有,push/pop/replace 都在它上面调,转场与返回手势由框架接管。
⚠️ 坑:目的页忘了包 NavDestination,页面能 push 但标题栏与返回行为都不对。
@ohos.router(旧路线)
import { router } from '@kit.ArkUI'
router.pushUrl({ url: 'pages/Detail' }) // 目标页必须先注册
命令式跳转,直观但扩展性差,官方已停止加新能力。
⚠️ 坑:目标页面必须注册进 resources/base/profile/main_pages.json,漏注册直接报错。
生命周期
分三层:UIAbility(应用级)、页面(@Entry)、组件(@Component),回调全靠覆写方法。
组件:aboutToAppear / aboutToDisappear
@Component
struct Feed {
aboutToAppear() { /* build 之前:初始化状态、发首个请求 */ }
aboutToDisappear() { /* 销毁前:取消订阅、释放资源 */ }
build() { /* ... */ }
}
组件创建与销毁的两端,等价于 React 的 mount/unmount effect。
⚠️ 坑:把请求写在 build() 里——build 会被框架多次调用,副作用成倍执行。
页面:onPageShow / onPageHide / onBackPress
@Entry
@Component
struct Index {
onPageShow() { /* 每次页面回到前台触发,含路由返回 */ }
onBackPress(): boolean { return false } // 返回 true 表示已拦截返回键
build() { /* ... */ }
}
页面级可见性回调,只对 @Entry 页面有效。
⚠️ 坑:”从详情页返回列表”要刷新时,aboutToAppear 不触发(组件没销毁),要写 onPageShow。
UIAbility:onCreate → onWindowStageCreate → onForeground/onBackground → onDestroy
// EntryAbility.ets 里覆写;onWindowStageCreate 中 loadContent('pages/Index')
onForeground(): void { /* 应用回到前台 */ }
onBackground(): void { /* 应用退到后台:停轮询、保存草稿 */ }
应用进程级回调,频率低,适合放全局资源管理。
并发
ArkTS 事件循环与 UI 隔离,多线程只有两条路——TaskPool(任务)与 Worker(线程),数据都经序列化拷贝传递。
taskpool + @Concurrent
import { taskpool } from '@kit.ArkTS'
@Concurrent
function heavyParse(raw: string): number { // 独立线程执行:不能碰 UI 与状态
return raw.split(',').length
}
taskpool.execute(heavyParse, input) // 返回 Promise
.then((n: number) => console.info(`${n} 项`))
一次性计算/IO 任务的首选,线程池自动管理复用,官方默认推荐。
⚠️ 坑:@Concurrent 函数里访问组件状态或全局单例——跨线程数据必须靠参数传值与返回值。
Worker
import { worker } from '@kit.ArkTS'
const w = new worker.ThreadWorker('entry/ets/workers/Calc.ets')
w.postMessage(data) // 双向消息通信
w.onmessage = (e) => { /* e.data 为回传结果 */ }
w.terminate() // 生命周期手动管理
长驻线程:持续通信、后台循环这类”活着比完成任务重要”的场景。
⚠️ 坑:路径写错启动即崩;忘记 terminate 是泄漏。口诀:任务用 TaskPool,线程用 Worker。
网络与权限
权限先声明后使用,落点在 module.json5。环境与签名问题回《鸿蒙开发环境搭建》。
module.json5 权限声明
{
"module": {
"requestPermissions": [
// system_grant:安装即授予,只需 name
{ "name": "ohos.permission.INTERNET" },
// user_grant:需 reason + usedScene,运行时弹窗申请
{
"name": "ohos.permission.CAMERA",
"reason": "$string:camera_reason",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
}
]
}
}
权限分 system_grant(声明即得)与 user_grant(运行时申请)两类。
⚠️ 坑:user_grant 权限只声明不运行时申请,调 API 直接失败;reason 引用的字符串资源不存在会在打包时报错。
@ohos.net.http 请求
import { http } from '@kit.NetworkKit'
function getJson(url: string): Promise<string> {
const client = http.createHttp()
return client.request(url, { method: http.RequestMethod.GET })
.then((resp: http.HttpResponse) => {
if (resp.responseCode === 200) {
return resp.result as string // result 是 string | Object | ArrayBuffer
}
throw new Error(`HTTP ${resp.responseCode}`)
})
.finally(() => client.destroy()) // 用完销毁
}
系统 HTTP 客户端,Promise 风格,复杂项目可再封装。
⚠️ 坑:权限没声明时请求直接抛错,不是慢;明文 HTTP 可能被安全策略拦截,调试优先 HTTPS,放行配置以官方《网络安全配置》文档为准。
常见报错速查
| 现象 / 报错 | 常见原因 | 修法 |
|---|---|---|
| 改了数据界面不动 | @State 只观测第一层 | 整对象替换;V2 @ObservedV2/@Trace;或 @Observed+@ObjectLink |
| ForEach 增删后列表错乱 | key 缺失、用下标或 key 重复 | keyGenerator 返回稳定业务 id |
| “装饰器不适用”类报错 | V1/V2 混用(@Component 里写 @Local 等) | 组件整体迁 V2,或退回 V1 |
| arkts-no-untyped-obj-literals | 对象字面量没有类型归属 | 给变量或字面量标注类型(class/interface) |
| arkts-no-structural-typing | 类型仅”长得像”就互相赋值 | 显式 implements / extends 建立继承关系 |
| http 请求抛错 / 一直失败 | 没声明 INTERNET 权限 | requestPermissions 加 ohos.permission.INTERNET 后重装 |
| 跳转页面报”页面未注册” | 新页面没写进 main_pages.json | 在 main_pages.json 的 src 数组补路径 |
| 真机安装失败 | 未签名 / 证书过期 / 设备未入 Profile | 重新自动签名;查证书额度与 USB 调试 |
| 预览器白屏或”不支持预览” | 页面用了预览器不支持的系统 API | 换模拟器或真机验证 |
| @Concurrent 函数里改 UI 状态无效 | 子线程无法访问 UI 与状态 | 结果经 Promise 返回主线程再更新 |
速查表
| 条目 | 一句话用途 | 有什么坑 |
|---|---|---|
| @State (V1) | 组件内部状态 | 只观测第一层 |
| @Prop (V1) | 父到子单向拷贝 | 深拷贝开销 |
| @Link (V1) | 父子双向同步 | 父侧传值要加 $ |
| @Provide/@Consume (V1) | 跨层级共享 | 同名匹配,重名覆盖 |
| @Observed+@ObjectLink (V1) | 嵌套对象观测 | @ObjectLink 不能本地初始化 |
| @Local (V2) | 取代 @State | 不能与 V1 装饰器混用 |
| @Param+@Event (V2) | 只读入参 + 显式回调 | @Param 内部赋值即错 |
| @ObservedV2+@Trace (V2) | 属性级深度观测 | 漏标 @Trace 是盲区 |
| @Monitor (V2) | 声明式监听变化 | 参数是变量名字符串 |
| Column/Row/Stack | 三大布局容器 | 占位用 layoutWeight 不是 flex |
| List+ForEach | 滚动列表 | key 用下标必错乱 |
| Grid | 网格布局 | 不配 columnsTemplate 不成网格 |
| Navigation/NavPathStack | 官方推荐导航 | 目的页要包 NavDestination |
| @ohos.router | 旧页面路由 | 停止演进,新项目别选 |
| aboutToAppear | 组件初始化 | 副作用别放 build |
| onPageShow | 页面回前台 | 返回刷新场景用它 |
| UIAbility 生命周期 | 应用级回调 | onBackground 里保存状态 |
| taskpool+@Concurrent | 一次性并发任务 | 子线程摸不到 UI |
| Worker | 长驻线程 | 手动 terminate |
| http 模块 | 系统网络请求 | 权限未声明直接抛错 |
| requestPermissions | module.json5 权限声明 | user_grant 还要运行时申请 |
下一步
字典覆盖入门到日常开发的查表需求,定制动画、NDK、跨设备流转等深水区不在其列,以官方文档为准。实感来自《第一个鸿蒙应用》,语言约束见《ArkTS 语法要点》;写过 Flutter 的读者可对照《Flutter 基础知识参考字典》。
版本核实(撰写时检索,2025-01):V1 状态装饰器为 API 9 起体系,V2(@ComponentV2/@Local/@Param/@Event/@ObservedV2/@Trace/@Monitor)自 API 12 起提供且为官方主推方向,两套不可混用;@ohos.router 不再获得新能力、官方推荐 Navigation;并发推荐 TaskPool;http 需声明 ohos.permission.INTERNET。当前 Release SDK 配套 HarmonyOS NEXT 5.0(API 12),API 可用性与参数细节以 developer.huawei.com 官方文档为准。