嵌入式鸿蒙系统开发速成快速学习第一部份,基础组件
鸿蒙 ArkTS 快速学习文档
适用版本:HarmonyOS NEXT / API 12+
开发语言:ArkTS(TypeScript 超集)
IDE:DevEco Studio
本文档定位:跟着念、跟着敲、直接上课用
目录
一、开发环境快速搭建
1.1 安装 DevEco Studio
-
安装 Node.js(建议 18.x 以上,IDE 自带也可)
-
首次启动 → 配置 SDK → 下载 HarmonyOS SDK(API 12)
-
创建项目:
-
File → New → Create Project
-
选择 Empty Ability → 语言选 ArkTS → 设备选 Phone
-
包名格式:
com.example.myapplication
-
1.2 工程目录结构
plain
MyApplication/
├── entry/src/main/ets/ // 源码目录
│ ├── entryability/ // Ability(页面入口)
│ │ └── EntryAbility.ets
│ ├── pages/ // 页面目录
│ │ └── Index.ets // 首页
│ └── entrybackupability/ // 备份恢复 Ability
├── entry/src/main/resources/ // 资源文件
│ ├── base/element/ // 颜色、字符串等常量
│ ├── base/media/ // 图片资源
│ └── rawfile/ // 原始文件(json、txt等)
├── entry/src/main/module.json5 // 模块配置
└── build-profile.json5 // 编译配置
常用插件开发用
DevEco Studio 常用插件清单
鸿蒙官方
表格
| 插件名 | 一句话说明 |
|---|---|
| HarmonyOS Component Center | 官方组件/模板市场,IDE 内直接下载鸿蒙组件 |
| DevEco CodeGenie | 华为官方 AI 辅助编程,代码补全与生成 |
| DevEco Device Connector | 真机/模拟器连接调试管理 |
| HMS Core Kit | 集成华为移动服务(地图、支付、推送等) |
代码效率
表格
| 插件名 | 一句话说明 |
|---|---|
| Rainbow Brackets | 彩虹括号,区分嵌套层级 |
| CodeGlance | 编辑器右侧代码缩略图,大文件快速定位 |
| Indent Rainbow | 彩虹缩进,不同作用域显示不同颜色 |
| Code Spell Checker | 实时检查代码拼写错误 |
| ESLint | JS/ArkTS 代码规范检查 |
版本控制
表格
| 插件名 | 一句话说明 |
|---|---|
| Git Integration | 内置图形化 Git 操作 |
| GitLens | 行内显示代码作者、修改时间、提交信息 |
UI / 资源
表格
| 插件名 | 一句话说明 |
|---|---|
| Color Designer | 调色板工具,一键生成 ArkTS 颜色代码 |
| Image Assistant | 图片资源管理辅助 |
| Localization Generator | 多语言资源快速生成 |
文档 / 辅助
表格
| 插件名 | 一句话说明 |
|---|---|
| Markdown Support | Markdown 编辑与实时预览 |
| JSON Viewer | JSON 格式化、美化、节点折叠 |
| Statistic | 代码统计(行数、文件数) |
1.3 第一个页面(Hello Harmony)
TypeScript
// entry/src/main/ets/pages/Index.ets
@Entry // 标记为页面入口
@Component // 标记为自定义组件
struct Index {
@State message: string = 'Hello HarmonyOS'
build() {
Column() { // 垂直排列容器
Text(this.message)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FF6600')
Button('点击我')
.width(200)
.height(50)
.backgroundColor('#007DFF')
.onClick(() => {
this.message = '鸿蒙,来了!'
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
核心概念三件套:
-
@Entry:页面入口,每个页面必须有且仅有一个 -
@Component:自定义组件装饰器 -
struct:ArkTS 中定义组件的结构体关键字 -
@State:组件内部状态,数据变化自动刷新 UI
二、ArkTS 基础语法速览
2.1 数据类型与变量声明
TypeScript
// 基本类型
let userName: string = '张三'
let age: number = 25
let isVip: boolean = true
let score: number | string = 100 // 联合类型:可以是 number 或 string
let anything: any = '任意类型'
// 常量
const PI: number = 3.14159
const APP_NAME: string = '鸿蒙商城'
// 类型推断(可省略类型注解)
let price = 99.9 // 自动推断为 number
let title = '商品标题' // 自动推断为 string
2.2 数组与函数
TypeScript
// 数组声明
let colors: string[] = ['红', '绿', '蓝']
let scores: Array<number> = [85, 90, 78]
// 数组常用操作
colors.push('黄') // 添加元素
let first = colors[0] // 读取:'红'
let len = colors.length // 长度:4
let sliced = colors.slice(0, 2) // 截取:['红','绿']
// 函数声明(三种写法)
// 写法1:标准函数
function add(a: number, b: number): number {
return a + b
}
// 写法2:箭头函数(推荐,尤其在回调中)
const multiply = (a: number, b: number): number => {
return a * b
}
// 写法3:匿名函数(常用于事件回调)
button.onClick(() => {
console.info('按钮被点击')
})
// 可选参数与默认值
function greet(name: string, greeting?: string): string {
return `${greeting || '你好'},${name}`
}
// 剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((acc, cur) => acc + cur, 0)
}
2.3 对象、接口与枚举
TypeScript
// 接口定义(描述对象结构)
interface Product {
id: number
name: string
price: number
isOnSale?: boolean // 可选属性
}
// 对象创建
let phone: Product = {
id: 1001,
name: 'Mate 70 Pro',
price: 6999,
isOnSale: true
}
// 枚举(Enum)—— 用于定义一组命名常量
enum OrderStatus {
PENDING = '待支付',
PAID = '已支付',
SHIPPED = '已发货',
COMPLETED = '已完成'
}
let currentStatus: OrderStatus = OrderStatus.PAID
console.info(currentStatus) // 输出:已支付
// 枚举在 UI 中的典型用法
enum ThemeColor {
PRIMARY = '#007DFF',
SUCCESS = '#00B578',
WARNING = '#FF9500',
DANGER = '#FA2C2C'
}
2.4 联合类型与类型守卫
TypeScript
// 联合类型:一个变量可以是多种类型之一
let value: string | number = 'hello'
value = 100 // 合法
// 类型守卫:运行时判断具体类型
function processValue(val: string | number) {
if (typeof val === 'string') {
console.info('字符串长度:', val.length)
} else {
console.info('数字平方:', val * val)
}
}
三、常用组件与属性详解
3.1 文本组件 Text
TypeScript
Text('普通文本')
.fontSize(16) // 字体大小
.fontColor('#333333') // 字体颜色
.fontWeight(FontWeight.Bold) // 字重:Lighter/Normal/Bold/Bolder
.fontFamily('HarmonyOS Sans') // 字体
.lineHeight(24) // 行高
.maxLines(2) // 最大行数
.textOverflow({ overflow: TextOverflow.Ellipsis }) // 超出省略
.textAlign(TextAlign.Center) // 对齐:Start/Center/End
.decoration({ type: TextDecorationType.Underline, color: Color.Red }) // 下划线
3.2 按钮组件 Button
TypeScript
Button('确认提交', { type: ButtonType.Capsule }) // 胶囊按钮
.width('80%')
.height(48)
.fontSize(16)
.fontColor(Color.White)
.backgroundColor('#007DFF')
.stateEffect(true) // 点击态效果
.onClick(() => {
console.info('提交按钮点击')
})
// 带图标的按钮
Button() {
Row() {
Image($r('app.media.icon_search')).width(20).height(20)
Text('搜索').fontSize(14).margin({ left: 8 })
}
}
.width(120)
.height(40)
.backgroundColor('#F5F5F5')
3.3 图片组件 Image
TypeScript
// 加载本地资源(resources/base/media/ 下的图片)
Image($r('app.media.logo'))
.width(100)
.height(100)
.borderRadius(50) // 圆角 → 圆形
.objectFit(ImageFit.Cover) // 填充模式:Contain/Cover/Fill/None/ScaleDown
.interpolation(ImageInterpolation.High) // 插值质量
// 加载网络图片(需配置网络权限)
Image('https://example.com/avatar.png')
.width(120)
.height(120)
.alt($r('app.media.placeholder')) // 占位图
// 加载像素图(PixelMap,常用于相机、截图)
Image(pixelMap)
.width('100%')
.height(200)
3.4 输入框 TextInput / TextArea
TypeScript
@State username: string = ''
@State password: string = ''
TextInput({ placeholder: '请输入用户名', text: $$this.username })
.width('90%')
.height(48)
.backgroundColor('#F5F5F5')
.borderRadius(8)
.padding({ left: 16, right: 16 })
.fontSize(14)
.maxLength(20) // 最大长度
.inputFilter('[a-zA-Z0-9]', (value: string) => {
// 输入过滤:只允许字母数字
})
TextInput({ placeholder: '请输入密码', text: $$this.password })
.type(InputType.Password) // 密码输入模式
.showPasswordIcon(true) // 显示密码可见切换图标
.width('90%')
.height(48)
// 多行文本输入
TextArea({ placeholder: '请输入描述', text: $$this.description })
.width('90%')
.height(120)
.backgroundColor('#F8F8F8')
3.5 开关与选择器
TypeScript
@State isOn: boolean = false
@State selectedIndex: number = 0
// Toggle 开关
Toggle({ type: ToggleType.Switch, isOn: $$this.isOn })
.selectedColor('#007DFF')
.onChange((isOn: boolean) => {
console.info('开关状态:', isOn)
})
// Checkbox 多选框
Checkbox()
.select(this.isChecked)
.selectedColor('#007DFF')
.onChange((value: boolean) => {
this.isChecked = value
})
// Slider 滑块
Slider({ value: 50, min: 0, max: 100, step: 1 })
.width('80%')
.blockColor('#007DFF')
.trackColor('#E5E5E5')
.selectedColor('#007DFF')
.onChange((value: number, mode: SliderChangeMode) => {
console.info('当前值:', value)
})
// Radio 单选
Radio({ value: 'male', group: 'gender' })
.checked(this.gender === 'male')
.onChange(() => { this.gender = 'male' })
3.6 进度条与加载
TypeScript
// 进度条
Progress({ value: 60, total: 100, type: ProgressType.Linear })
.width('80%')
.height(8)
.color('#007DFF')
.backgroundColor('#E5E5E5')
// 环形进度条
Progress({ value: 75, total: 100, type: ProgressType.Ring })
.width(80)
.height(80)
.color('#007DFF')
// Loading 加载动画
LoadingProgress()
.width(40)
.height(40)
.color('#007DFF')
3.7 列表 List / ListItem
TypeScript
@State productList: Product[] = [
{ id: 1, name: '手机', price: 3999 },
{ id: 2, name: '平板', price: 2999 },
{ id: 3, name: '手表', price: 1999 }
]
List({ space: 12 }) { // 列表项间距
ForEach(this.productList, (item: Product, index: number) => {
ListItem() {
Row() {
Column() {
Text(item.name)
.fontSize(16)
.fontWeight(FontWeight.Medium)
Text(`¥${item.price}`)
.fontSize(14)
.fontColor('#FF6600')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Button('购买')
.width(80)
.height(36)
.fontSize(14)
.backgroundColor('#007DFF')
}
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(12)
}
.swipeAction({ end: this.DeleteBuilder(item) }) // 侧滑删除
}, (item: Product) => item.id.toString()) // 键值函数,优化渲染
}
.width('100%')
.height('100%')
.padding(16)
.backgroundColor('#F5F5F5')
.listDirection(Axis.Vertical) // 排列方向
.divider({ strokeWidth: 1, color: '#EEEEEE' }) // 分割线
.edgeEffect(EdgeEffect.Spring) // 边缘弹性效果
.scrollBar(BarState.Auto) // 滚动条
// 侧滑删除按钮构建器
@Builder
DeleteBuilder(item: Product) {
Button() {
Image($r('app.media.icon_delete')).width(24).height(24).fillColor(Color.White)
}
.width(60)
.height('100%')
.backgroundColor('#FA2C2C')
.onClick(() => {
this.productList = this.productList.filter(p => p.id !== item.id)
})
}
3.8 滚动容器 Scroll
TypeScript
Scroll() {
Column() {
// 大量内容...
ForEach(this.dataList, (item: string) => {
Text(item)
.width('100%')
.height(60)
.backgroundColor('#F8F8F8')
.margin({ bottom: 8 })
})
}
.width('100%')
}
.width('100%')
.height('100%')
.scrollable(ScrollDirection.Vertical) // 垂直滚动
.scrollBar(BarState.On) // 始终显示滚动条
.edgeEffect(EdgeEffect.Spring) // 弹性边缘
.onScroll((xOffset: number, yOffset: number) => {
console.info('滚动偏移:', yOffset)
})
3.9 标签页 Tabs
TypeScript
@State currentIndex: number = 0
Tabs({ barPosition: BarPosition.End }) { // 底部导航栏
TabContent() {
HomePage() // 首页内容
}
.tabBar(this.TabBuilder('首页', 0, $r('app.media.icon_home')))
TabContent() {
CategoryPage() // 分类内容
}
.tabBar(this.TabBuilder('分类', 1, $r('app.media.icon_category')))
TabContent() {
CartPage() // 购物车内容
}
.tabBar(this.TabBuilder('购物车', 2, $r('app.media.icon_cart')))
TabContent() {
MinePage() // 我的内容
}
.tabBar(this.TabBuilder('我的', 3, $r('app.media.icon_mine')))
}
.width('100%')
.height('100%')
.barWidth('100%')
.barHeight(56)
.onChange((index: number) => {
this.currentIndex = index
})
// 自定义 TabBar 构建器
@Builder
TabBuilder(title: string, targetIndex: number, icon: Resource) {
Column() {
Image(icon)
.width(24)
.height(24)
.fillColor(this.currentIndex === targetIndex ? '#007DFF' : '#999999')
Text(title)
.fontSize(12)
.fontColor(this.currentIndex === targetIndex ? '#007DFF' : '#999999')
.margin({ top: 4 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
3.10 轮播图 Swiper
TypeScript
@State bannerList: string[] = [
'https://example.com/banner1.jpg',
'https://example.com/banner2.jpg',
'https://example.com/banner3.jpg'
]
Swiper() {
ForEach(this.bannerList, (url: string, index: number) => {
Image(url)
.width('100%')
.height(180)
.objectFit(ImageFit.Cover)
.borderRadius(12)
})
}
.width('100%')
.height(180)
.indicator(true) // 显示指示器
.autoPlay(true) // 自动播放
.interval(3000) // 轮播间隔 3 秒
.loop(true) // 循环播放
.duration(500) // 切换动画时长
.itemSpace(12) // 项间距
.onChange((index: number) => {
console.info('当前轮播索引:', index)
})
3.11 徽章 Badge
TypeScript
Badge({
value: '99+', // 徽章内容
position: BadgePosition.RightTop,
style: { badgeSize: 16, badgeColor: '#FA2C2C' }
}) {
Image($r('app.media.icon_message'))
.width(32)
.height(32)
}
// 小红点模式
Badge({
value: '',
position: BadgePosition.RightTop,
style: { badgeSize: 8, badgeColor: '#FA2C2C' }
}) {
Text('消息')
.fontSize(14)
}
四、布局系统(基础 → 高阶)
4.1 线性布局 Column / Row
TypeScript
// Column:垂直排列(主轴 Y,交叉轴 X)
Column({ space: 16 }) { // 子组件间距 16
Text('标题').fontSize(20).fontWeight(FontWeight.Bold)
Text('内容1').fontSize(14)
Text('内容2').fontSize(14)
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center) // 交叉轴对齐:Start/Center/End
.justifyContent(FlexAlign.Center) // 主轴对齐
.padding(20)
.backgroundColor('#F5F5F5')
// Row:水平排列(主轴 X,交叉轴 Y)
Row({ space: 12 }) {
Image($r('app.media.avatar')).width(48).height(48).borderRadius(24)
Column() {
Text('用户名').fontSize(16).fontWeight(FontWeight.Medium)
Text('在线').fontSize(12).fontColor('#999999')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1) // 占据剩余空间
Button('关注')
.width(80)
.height(36)
.fontSize(14)
.backgroundColor('#007DFF')
}
.width('100%')
.height(72)
.padding({ left: 16, right: 16 })
.backgroundColor(Color.White)
4.2 弹性布局 Flex
TypeScript
Flex({
direction: FlexDirection.Row, // 方向:Row/RowReverse/Column/ColumnReverse
wrap: FlexWrap.Wrap, // 换行:NoWrap/Wrap/WrapReverse
justifyContent: FlexAlign.SpaceBetween, // 主轴对齐
alignItems: ItemAlign.Center // 交叉轴对齐
}) {
ForEach(this.tagList, (tag: string) => {
Text(tag)
.fontSize(14)
.padding({ top: 6, bottom: 6, left: 12, right: 12 })
.backgroundColor('#F0F0F0')
.borderRadius(16)
.margin({ bottom: 8, right: 8 })
})
}
.width('100%')
.padding(16)
4.3 层叠布局 Stack / Position
TypeScript
Stack({ alignContent: Alignment.BottomEnd }) {
Image($r('app.media.cover'))
.width('100%')
.height(200)
.objectFit(ImageFit.Cover)
.borderRadius(12)
Text('VIP')
.fontSize(12)
.fontColor(Color.White)
.backgroundColor('#FF9500')
.padding({ top: 4, bottom: 4, left: 8, right: 8 })
.borderRadius({ topLeft: 12, bottomRight: 12 })
.position({ x: 0, y: 0 }) // 绝对定位
}
.width('100%')
.height(200)
// 使用 Position 绝对定位
Column() {
Text('底层内容').width('100%').height('100%')
Button('悬浮按钮')
.width(56)
.height(56)
.backgroundColor('#007DFF')
.position({ x: '80%', y: '80%' }) // 百分比定位
}
.width('100%')
.height('100%')
4.4 网格布局 Grid
TypeScript
Grid() {
ForEach(this.productList, (item: Product) => {
GridItem() {
Column() {
Image(item.image)
.width('100%')
.aspectRatio(1) // 保持 1:1 比例
.objectFit(ImageFit.Cover)
.borderRadius(8)
Text(item.name)
.fontSize(14)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 8 })
Text(`¥${item.price}`)
.fontSize(16)
.fontColor('#FF6600')
.fontWeight(FontWeight.Bold)
}
.width('100%')
.padding(8)
.backgroundColor(Color.White)
.borderRadius(12)
}
})
}
.width('100%')
.columnsTemplate('1fr 1fr') // 两列等宽
.rowsGap(12) // 行间距
columnsGap(12) // 列间距
.padding(16)
.backgroundColor('#F5F5F5')
4.5 相对布局 RelativeContainer
TypeScript
RelativeContainer() {
Text('左上角')
.id('topLeft')
.fontSize(14)
.alignRules({
top: { anchor: '__container__', align: VerticalAlign.Top },
left: { anchor: '__container__', align: HorizontalAlign.Start }
})
Text('右上角')
.id('topRight')
.fontSize(14)
.alignRules({
top: { anchor: '__container__', align: VerticalAlign.Top },
right: { anchor: '__container__', align: HorizontalAlign.End }
})
Text('居中')
.id('center')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
Text('跟随左上角下方')
.id('below')
.fontSize(14)
.alignRules({
top: { anchor: 'topLeft', align: VerticalAlign.Bottom },
left: { anchor: 'topLeft', align: HorizontalAlign.Start }
})
.margin({ top: 8 })
}
.width('100%')
.height(300)
.backgroundColor('#F8F8F8')
五、样式系统与复用机制
5.1 通用属性(所有组件可用)
TypeScript
// 尺寸
.width(100) / .width('50%') / .width('100%')
.height(48)
.size({ width: 100, height: 48 }) // 同时设置宽高
.aspectRatio(1) // 宽高比
.layoutWeight(1) // 权重分配剩余空间
// 位置
.position({ x: 10, y: 20 }) // 绝对定位
.offset({ x: 10, y: 20 }) // 相对偏移
.markAnchor({ x: 0.5, y: 0.5 }) // 锚点
// 背景
.backgroundColor('#FFFFFF')
.backgroundColor(Color.Transparent)
.backgroundImage($r('app.media.bg'))
.backgroundImageSize(ImageSize.Cover)
// 边框
.border({
width: 1,
color: '#E5E5E5',
style: BorderStyle.Solid, // Solid/Dashed/Dotted
radius: 12 // 圆角
})
.borderRadius(12) // 简写
.borderWidth(1)
.borderColor('#E5E5E5')
// 阴影(高阶)
.shadow({
radius: 10, // 模糊半径
color: 'rgba(0,0,0,0.1)', // 阴影颜色
offsetX: 0, // X 偏移
offsetY: 4 // Y 偏移
})
// 内外边距
.padding(16) // 四边相同
.padding({ top: 8, bottom: 8, left: 16, right: 16 })
.margin(16)
.margin({ top: 8, bottom: 0, left: 16, right: 16 })
// 可见性与层级
.visibility(Visibility.Visible) // Visible/Hidden/None
.opacity(0.5) // 透明度 0~1
.zIndex(10) // 层级
5.2 样式复用 @Styles
TypeScript
// 定义可复用的样式函数
@Styles
function cardStyle() {
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 8, color: 'rgba(0,0,0,0.06)', offsetX: 0, offsetY: 2 })
}
@Styles
function primaryButtonStyle() {
.width('90%')
.height(48)
.fontSize(16)
.fontColor(Color.White)
.backgroundColor('#007DFF')
.borderRadius(8)
}
// 使用
Column() {
Text('卡片标题').fontSize(18).fontWeight(FontWeight.Bold)
Text('卡片内容').fontSize(14).fontColor('#666666').margin({ top: 8 })
}
.cardStyle() // 直接调用
.margin({ bottom: 12 })
Button('立即购买')
.primaryButtonStyle()
5.3 结构复用 @Builder
TypeScript
// 局部 Builder(定义在组件内部)
@Builder
ProductCard(item: Product) {
Row() {
Image(item.image)
.width(80)
.height(80)
.borderRadius(8)
.objectFit(ImageFit.Cover)
Column() {
Text(item.name).fontSize(16).fontWeight(FontWeight.Medium)
Text(`¥${item.price}`).fontSize(18).fontColor('#FF6600').margin({ top: 8 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
.margin({ left: 12 })
}
.width('100%')
.padding(12)
.backgroundColor(Color.White)
.borderRadius(12)
}
// 全局 Builder(定义在组件外部,可跨组件复用)
@Builder
export function GlobalProductCard(item: Product) {
// ... 同上
}
// 使用
List() {
ForEach(this.productList, (item: Product) => {
ListItem() {
this.ProductCard(item) // 调用局部 Builder
// 或 GlobalProductCard(item) // 调用全局 Builder
}
})
}
5.4 动画与过渡
TypeScript
@State isExpand: boolean = false
Column() {
Text('点击展开')
.fontSize(16)
.onClick(() => {
this.isExpand = !this.isExpand
})
if (this.isExpand) {
Column() {
Text('展开的内容1')
Text('展开的内容2')
Text('展开的内容3')
}
.width('100%')
.padding(16)
.backgroundColor('#F8F8F8')
.transition(TransitionEffect.asymmetric(
TransitionEffect.OPACITY.combine(TransitionEffect.translate({ y: -20 })),
TransitionEffect.OPACITY
)) // 进入/退出非对称动画
}
}
.width('100%')
// 属性动画
Text('缩放文字')
.fontSize(20)
.scale({ x: this.isExpand ? 1.2 : 1, y: this.isExpand ? 1.2 : 1 })
.animation({
duration: 300,
curve: Curve.EaseInOut,
iterations: 1,
playMode: PlayMode.Normal
})
// 显式动画
Button('播放动画')
.onClick(() => {
animateTo({
duration: 500,
curve: Curve.Spring,
onFinish: () => {
console.info('动画结束')
}
}, () => {
this.offsetY = 100 // 在闭包中修改状态,触发动画
})
})
六、页面与组件生命周期
6.1 页面生命周期(Page)
TypeScript
import { router } from '@kit.ArkUI'
@Entry
@Component
struct Index {
@State message: string = ''
// ========== 页面生命周期 ==========
// 1. 页面创建时触发(只触发一次)
aboutToAppear() {
console.info('【生命周期】aboutToAppear:页面即将出现')
// 适合:初始化数据、请求首屏数据、注册监听
this.loadData()
}
// 2. 页面显示时触发(每次从后台回到前台都会触发)
onPageShow() {
console.info('【生命周期】onPageShow:页面显示')
// 适合:刷新数据、恢复页面状态
}
// 3. 页面隐藏时触发(切换到其他页面或按 Home 键)
onPageHide() {
console.info('【生命周期】onPageHide:页面隐藏')
// 适合:暂停计时器、保存草稿
}
// 4. 页面销毁前触发(只触发一次)
aboutToDisappear() {
console.info('【生命周期】aboutToDisappear:页面即将销毁')
// 适合:取消网络请求、清除定时器、解绑监听
}
// 5. 返回键拦截(可选)
onBackPress(): boolean {
console.info('【生命周期】onBackPress:返回键被按下')
// return true 表示拦截返回,不退出页面
// return false 表示不拦截,正常退出
return false
}
async loadData() {
// 模拟网络请求
this.message = '数据加载完成'
}
build() {
Column() {
Text(this.message)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Button('跳转详情页')
.onClick(() => {
router.pushUrl({
url: 'pages/Detail',
params: { id: 123, name: '商品A' }
})
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
6.2 自定义组件生命周期(Component)
TypeScript
@Component
struct MyComponent {
@State count: number = 0
private timerId: number = -1
// 组件创建时
aboutToAppear() {
console.info('组件 aboutToAppear')
// 启动定时器
this.timerId = setInterval(() => {
this.count++
}, 1000)
}
// 组件销毁前
aboutToDisappear() {
console.info('组件 aboutToDisappear')
// 必须清理!防止内存泄漏
clearInterval(this.timerId)
}
// 组件布局完成时(API 12+)
onDidBuild() {
console.info('组件 onDidBuild:布局已完成')
}
build() {
Text(`计数:${this.count}`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
}
}
6.3 生命周期完整流程图
plain
页面A(Index)
│
├─ aboutToAppear() ← 初始化数据、请求首屏
├─ build() ← 构建 UI
├─ onDidBuild() ← 布局完成
├─ onPageShow() ← 页面可见
│
│ 【用户点击跳转】
│
├─ onPageHide() ← 页面隐藏(还在栈中)
│
│ 【用户按返回键】
│
├─ onPageShow() ← 页面重新显示
│
│ 【用户关闭页面】
│
└─ aboutToDisappear() ← 清理资源、取消请求
七、数据更新与状态管理
7.1 状态装饰器速查表
表格
| 装饰器 | 作用范围 | 数据流向 | 使用场景 |
|---|---|---|---|
@State |
组件内部 | 内部可变 | 组件私有状态(开关、计数) |
@Prop |
父 → 子 | 单向同步 | 父传子,子不可改 |
@Link |
父 ↔ 子 | 双向同步 | 父子共享状态(表单输入) |
@Provide / @Consume |
祖先 → 后代 | 双向同步 | 跨层级传值(主题、语言) |
@Observed + @ObjectLink |
嵌套对象 | 深度监听 | 对象数组内部属性变化 |
@Watch |
监听变化 | — | 状态变化时执行回调 |
7.2 @State — 组件内部状态
TypeScript
@Entry
@Component
struct CounterPage {
@State count: number = 0
@State userName: string = '张三'
build() {
Column({ space: 20 }) {
Text(`当前计数:${this.count}`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
Row({ space: 16 }) {
Button('-').onClick(() => { this.count-- })
Button('+').onClick(() => { this.count++ })
}
TextInput({ text: $$this.userName, placeholder: '输入名字' })
.width('80%')
.height(48)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
7.3 @Prop / @Link — 父子组件通信
TypeScript
// 父组件
@Entry
@Component
struct ParentPage {
@State parentCount: number = 10
build() {
Column({ space: 20 }) {
Text(`父组件:${this.parentCount}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
// @Prop:单向传递,子组件修改不影响父
ChildProp({ propCount: this.parentCount })
// @Link:双向绑定,子组件修改同步到父
ChildLink({ linkCount: this.parentCount })
Button('父组件+1')
.onClick(() => { this.parentCount++ })
}
.width('100%')
.padding(20)
}
}
// 子组件 — @Prop(单向)
@Component
struct ChildProp {
@Prop propCount: number // 从父接收,本地副本
build() {
Column() {
Text(`@Prop 接收:${this.propCount}`)
Button('子组件+1(不影响父)')
.onClick(() => { this.propCount++ }) // 只改本地
}
.padding(16)
.backgroundColor('#FFF3E0')
.borderRadius(8)
}
}
// 子组件 — @Link(双向)
@Component
struct ChildLink {
@Link linkCount: number // 与父共享引用
build() {
Column() {
Text(`@Link 绑定:${this.linkCount}`)
Button('子组件+1(同步父)')
.onClick(() => { this.linkCount++ }) // 同步修改父
}
.padding(16)
.backgroundColor('#E3F2FD')
.borderRadius(8)
}
}
7.4 @Provide / @Consume — 跨层级传值
TypeScript
// 祖先组件提供
@Entry
@Component
struct ThemePage {
@Provide('themeColor') themeColor: string = '#007DFF'
@Provide('fontSize') fontSize: number = 16
build() {
Column() {
GrandChild() // 深层嵌套的后代组件
}
}
}
// 后代组件消费(无论嵌套多深)
@Component
struct GrandChild {
@Consume('themeColor') themeColor: string
@Consume('fontSize') fontSize: number
build() {
Text('深层组件也能拿到主题')
.fontColor(this.themeColor)
.fontSize(this.fontSize)
}
}
7.5 @Observed + @ObjectLink — 对象深度监听
TypeScript
// 必须用 @Observed 标记类
@Observed
class Product {
id: number
name: string
price: number
isFavorite: boolean = false
constructor(id: number, name: string, price: number) {
this.id = id
this.name = name
this.price = price
}
}
@Entry
@Component
struct ProductListPage {
@State productList: Product[] = [
new Product(1, '手机', 3999),
new Product(2, '耳机', 899)
]
build() {
List() {
ForEach(this.productList, (item: Product) => {
ListItem() {
ProductItem({ product: item }) // 传入对象
}
})
}
}
}
@Component
struct ProductItem {
@ObjectLink product: Product // 深度监听对象属性变化
build() {
Row() {
Text(this.product.name)
Text(`¥${this.product.price}`)
Button(this.product.isFavorite ? '已收藏' : '收藏')
.onClick(() => {
this.product.isFavorite = !this.product.isFavorite // 触发 UI 刷新
})
}
}
}
7.6 @Watch — 监听状态变化
TypeScript
@Entry
@Component
struct WatchDemo {
@State @Watch('onCountChange') count: number = 0
@State message: string = '初始状态'
onCountChange() {
// count 变化时自动执行
if (this.count > 10) {
this.message = '计数超过10了!'
} else {
this.message = `当前计数:${this.count}`
}
console.info('count 变化为:', this.count)
}
build() {
Column({ space: 16 }) {
Text(this.message).fontSize(18).fontColor('#FF6600')
Text(`${this.count}`).fontSize(48).fontWeight(FontWeight.Bold)
Button('+1').onClick(() => { this.count++ })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
八、网络请求实战
8.1 配置网络权限
在
entry/src/main/module.json5 中添加:JSON
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
8.2 HTTP 请求封装
TypeScript
import { http } from '@kit.NetworkKit'
// 封装网络请求类
class HttpRequest {
private httpClient: http.HttpRequest
constructor() {
this.httpClient = http.createHttp()
}
// GET 请求
async get<T>(url: string, params?: Record<string, string>): Promise<T> {
let fullUrl = url
if (params) {
const query = Object.entries(params)
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&')
fullUrl += `?${query}`
}
try {
const response = await this.httpClient.request(fullUrl, {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' },
connectTimeout: 60000,
readTimeout: 60000
})
if (response.responseCode === 200) {
return JSON.parse(response.result.toString()) as T
} else {
throw new Error(`请求失败,状态码:${response.responseCode}`)
}
} catch (error) {
console.error('GET 请求错误:', JSON.stringify(error))
throw error
}
}
// POST 请求
async post<T>(url: string, data: object): Promise<T> {
try {
const response = await this.httpClient.request(url, {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your_token_here' // 如需 Token
},
extraData: JSON.stringify(data),
connectTimeout: 60000,
readTimeout: 60000
})
if (response.responseCode === 200) {
return JSON.parse(response.result.toString()) as T
} else {
throw new Error(`请求失败,状态码:${response.responseCode}`)
}
} catch (error) {
console.error('POST 请求错误:', JSON.stringify(error))
throw error
}
}
// 取消请求
destroy() {
this.httpClient.destroy()
}
}
// 导出单例
export const httpRequest = new HttpRequest()
8.3 实战:请求商品列表
TypeScript
import { httpRequest } from '../utils/HttpRequest'
// 定义数据接口
interface ApiResponse<T> {
code: number
message: string
data: T
}
interface Product {
id: number
name: string
price: number
image: string
}
@Entry
@Component
struct NetworkDemoPage {
@State productList: Product[] = []
@State isLoading: boolean = false
@State errorMsg: string = ''
aboutToAppear() {
this.fetchProducts()
}
aboutToDisappear() {
httpRequest.destroy() // 页面销毁时取消请求
}
async fetchProducts() {
this.isLoading = true
this.errorMsg = ''
try {
const res = await httpRequest.get<ApiResponse<Product[]>>(
'https://api.example.com/products',
{ page: '1', size: '10' }
)
if (res.code === 200) {
this.productList = res.data
} else {
this.errorMsg = res.message
}
} catch (err) {
this.errorMsg = '网络请求失败,请检查网络'
} finally {
this.isLoading = false
}
}
build() {
Column() {
if (this.isLoading) {
Column() {
LoadingProgress().width(40).height(40).color('#007DFF')
Text('加载中...').fontSize(14).fontColor('#999999').margin({ top: 12 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
} else if (this.errorMsg !== '') {
Column() {
Text(this.errorMsg).fontSize(16).fontColor('#FA2C2C')
Button('重新加载')
.margin({ top: 16 })
.onClick(() => { this.fetchProducts() })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
} else {
List({ space: 12 }) {
ForEach(this.productList, (item: Product) => {
ListItem() {
Row() {
Image(item.image)
.width(80)
.height(80)
.borderRadius(8)
.objectFit(ImageFit.Cover)
Column() {
Text(item.name).fontSize(16).fontWeight(FontWeight.Medium)
Text(`¥${item.price}`).fontSize(18).fontColor('#FF6600').margin({ top: 8 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
.margin({ left: 12 })
}
.width('100%')
.padding(12)
.backgroundColor(Color.White)
.borderRadius(12)
}
})
}
.width('100%')
.height('100%')
.padding(16)
.backgroundColor('#F5F5F5')
.refresh({
refreshing: $$this.isLoading,
builder: this.RefreshBuilder,
onRefresh: () => { this.fetchProducts() }
})
}
}
.width('100%')
.height('100%')
}
@Builder
RefreshBuilder() {
Row() {
LoadingProgress().width(24).height(24).color('#007DFF')
Text('下拉刷新').fontSize(14).fontColor('#999999').margin({ left: 8 })
}
.width('100%')
.height(60)
.justifyContent(FlexAlign.Center)
}
}
8.4 上传文件
TypeScript
import { request } from '@kit.BasicServicesKit'
async uploadFile(fileUri: string) {
const uploadConfig: request.UploadConfig = {
url: 'https://api.example.com/upload',
header: { 'Authorization': 'Bearer token' },
method: 'POST',
files: [{ filename: 'image.jpg', name: 'file', uri: fileUri, type: 'image/jpeg' }],
data: [{ name: 'description', value: '头像上传' }]
}
try {
const uploadTask = await request.uploadFile(getContext(), uploadConfig)
uploadTask.on('complete', (taskStates) => {
console.info('上传完成:', JSON.stringify(taskStates))
})
uploadTask.on('fail', (taskStates) => {
console.error('上传失败:', JSON.stringify(taskStates))
})
} catch (err) {
console.error('上传异常:', JSON.stringify(err))
}
}
8.5 下载文件
TypeScript
import { request } from '@kit.BasicServicesKit'
async downloadFile() {
const downloadConfig: request.DownloadConfig = {
url: 'https://example.com/file.pdf',
filePath: getContext().filesDir + '/downloads/report.pdf',
header: {}
}
try {
const downloadTask = await request.downloadFile(getContext(), downloadConfig)
downloadTask.on('complete', () => {
console.info('下载完成')
})
downloadTask.on('progress', (receivedSize, totalSize) => {
const progress = (receivedSize / totalSize) * 100
console.info(`下载进度:${progress.toFixed(2)}%`)
})
} catch (err) {
console.error('下载异常:', JSON.stringify(err))
}
}
九、综合案例实战
9.1 案例:简易电商首页
TypeScript
import { router } from '@kit.ArkUI'
import { httpRequest } from '../utils/HttpRequest'
interface Banner {
id: number
image: string
link: string
}
interface Category {
id: number
name: string
icon: string
}
interface Product {
id: number
name: string
price: number
originalPrice: number
image: string
tags: string[]
}
@Entry
@Component
struct HomePage {
@State bannerList: Banner[] = []
@State categoryList: Category[] = []
@State productList: Product[] = []
@State isLoading: boolean = true
@State scrollY: number = 0
aboutToAppear() {
this.loadHomeData()
}
async loadHomeData() {
// 模拟并发请求
await Promise.all([
this.loadBanners(),
this.loadCategories(),
this.loadProducts()
])
this.isLoading = false
}
async loadBanners() {
// 模拟数据
this.bannerList = [
{ id: 1, image: 'https://example.com/b1.jpg', link: '' },
{ id: 2, image: 'https://example.com/b2.jpg', link: '' },
{ id: 3, image: 'https://example.com/b3.jpg', link: '' }
]
}
async loadCategories() {
this.categoryList = [
{ id: 1, name: '手机', icon: 'icon_phone' },
{ id: 2, name: '电脑', icon: 'icon_pc' },
{ id: 3, name: '家电', icon: 'icon_home' },
{ id: 4, name: '服饰', icon: 'icon_clothes' },
{ id: 5, name: '食品', icon: 'icon_food' }
]
}
async loadProducts() {
this.productList = [
{ id: 1, name: 'Mate 70 Pro', price: 6999, originalPrice: 7999, image: '', tags: ['新品', '热销'] },
{ id: 2, name: 'FreeBuds Pro 3', price: 1299, originalPrice: 1499, image: '', tags: ['降噪'] }
]
}
build() {
Stack({ alignContent: Alignment.Top }) {
Scroll() {
Column({ space: 0 }) {
// 轮播图
this.BannerBuilder()
// 分类入口
this.CategoryBuilder()
// 商品列表标题
Row() {
Text('爆款推荐')
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text('查看更多 >')
.fontSize(14)
.fontColor('#999999')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding({ left: 16, right: 16, top: 16, bottom: 12 })
// 商品网格
this.ProductGridBuilder()
}
.width('100%')
}
.width('100%')
.height('100%')
.scrollBar(BarState.Off)
.onScroll((x, y) => {
this.scrollY = y
})
// 顶部导航栏(滚动后显示背景)
this.NavBarBuilder()
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
@Builder
NavBarBuilder() {
Row() {
Text('鸿蒙商城')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(this.scrollY > 100 ? '#333333' : Color.White)
Row() {
Image($r('app.media.icon_search'))
.width(20)
.height(20)
.fillColor(this.scrollY > 100 ? '#999999' : Color.White)
Text('搜索商品')
.fontSize(14)
.fontColor(this.scrollY > 100 ? '#999999' : 'rgba(255,255,255,0.8)')
.margin({ left: 8 })
}
.width(200)
.height(36)
.backgroundColor(this.scrollY > 100 ? '#F5F5F5' : 'rgba(255,255,255,0.2)')
.borderRadius(18)
.padding({ left: 12 })
.layoutWeight(1)
.margin({ left: 16, right: 16 })
Badge({
value: '3',
position: BadgePosition.RightTop,
style: { badgeSize: 14, badgeColor: '#FA2C2C' }
}) {
Image($r('app.media.icon_cart'))
.width(24)
.height(24)
.fillColor(this.scrollY > 100 ? '#333333' : Color.White)
}
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor(this.scrollY > 100 ? Color.White : Color.Transparent)
.shadow({
radius: this.scrollY > 100 ? 4 : 0,
color: 'rgba(0,0,0,0.1)',
offsetY: 2
})
}
@Builder
BannerBuilder() {
Swiper() {
ForEach(this.bannerList, (item: Banner) => {
Image(item.image)
.width('100%')
.height(200)
.objectFit(ImageFit.Cover)
})
}
.width('100%')
.height(200)
.indicator(true)
.autoPlay(true)
.interval(3000)
.loop(true)
}
@Builder
CategoryBuilder() {
Grid() {
ForEach(this.categoryList, (item: Category) => {
GridItem() {
Column({ space: 8 }) {
Image($r(`app.media.${item.icon}`))
.width(48)
.height(48)
.borderRadius(24)
Text(item.name).fontSize(13).fontColor('#666666')
}
.width('100%')
.padding({ top: 16, bottom: 16 })
}
})
}
.width('100%')
.height(100)
.columnsTemplate('1fr 1fr 1fr 1fr 1fr')
.backgroundColor(Color.White)
.margin({ top: 12 })
}
@Builder
ProductGridBuilder() {
Grid() {
ForEach(this.productList, (item: Product) => {
GridItem() {
Column() {
Stack({ alignContent: Alignment.TopStart }) {
Image(item.image)
.width('100%')
.aspectRatio(1)
.objectFit(ImageFit.Cover)
.borderRadius({ topLeft: 12, topRight: 12 })
if (item.tags.length > 0) {
Text(item.tags[0])
.fontSize(11)
.fontColor(Color.White)
.backgroundColor('#FF6600')
.padding({ top: 2, bottom: 2, left: 6, right: 6 })
.borderRadius({ topLeft: 12, bottomRight: 8 })
}
}
.width('100%')
Column({ space: 4 }) {
Text(item.name)
.fontSize(14)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width('100%')
Row() {
Text(`¥${item.price}`)
.fontSize(18)
.fontColor('#FF6600')
.fontWeight(FontWeight.Bold)
Text(`¥${item.originalPrice}`)
.fontSize(12)
.fontColor('#999999')
.decoration({ type: TextDecorationType.LineThrough })
.margin({ left: 8 })
}
.width('100%')
}
.width('100%')
.padding(12)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
}
.onClick(() => {
router.pushUrl({ url: 'pages/ProductDetail', params: { id: item.id } })
})
})
}
.width('100%')
.columnsTemplate('1fr 1fr')
.rowsGap(12)
.columnsGap(12)
.padding(16)
}
}
十、组件速查表
10.1 基础组件
表格
| 组件 | 核心属性 | 常用事件 |
|---|---|---|
Text |
fontSize, fontColor, fontWeight, maxLines, textOverflow |
— |
Image |
src, width, height, objectFit, borderRadius, alt |
onComplete, onError |
Button |
type, fontSize, backgroundColor, stateEffect |
onClick |
TextInput |
placeholder, type, maxLength, showPasswordIcon |
onChange, onSubmit |
TextArea |
placeholder, maxLength |
onChange |
LoadingProgress |
width, height, color |
— |
Progress |
value, total, type, color |
— |
10.2 选择组件
表格
| 组件 | 说明 | 关键属性 |
|---|---|---|
Toggle |
开关 | type: Switch/Checkbox, isOn, selectedColor |
Checkbox |
复选框 | select, selectedColor |
Radio |
单选框 | value, group, checked |
Slider |
滑块 | value, min, max, step |
DatePicker |
日期选择 | selected, lunar |
TimePicker |
时间选择 | selected |
10.3 容器组件
表格
| 组件 | 排列方式 | 核心属性 |
|---|---|---|
Column |
垂直 | space, alignItems, justifyContent |
Row |
水平 | space, alignItems, justifyContent |
Flex |
弹性 | direction, wrap, justifyContent, alignItems |
Stack |
层叠 | alignContent |
List |
列表 | space, divider, edgeEffect, scrollBar |
Grid |
网格 | columnsTemplate, rowsTemplate, columnsGap, rowsGap |
Scroll |
滚动 | scrollable, scrollBar, edgeEffect |
Swiper |
轮播 | autoPlay, interval, loop, indicator |
Tabs |
标签页 | barPosition, barWidth, barHeight |
RelativeContainer |
相对 | alignRules |
10.4 装饰器速查
表格
| 装饰器 | 用途 | 语法示例 |
|---|---|---|
@Entry |
页面入口 | @Entry struct Index {} |
@Component |
自定义组件 | @Component struct MyComp {} |
@State |
内部状态 | @State count: number = 0 |
@Prop |
父传子(单向) | @Prop title: string |
@Link |
父子双向 | @Link value: number |
@Provide |
祖先提供 | @Provide('theme') theme: string = 'light' |
@Consume |
后代消费 | @Consume('theme') theme: string |
@Observed |
类深度监听 | @Observed class User {} |
@ObjectLink |
对象链接 | @ObjectLink user: User |
@Watch |
监听变化 | @State @Watch('onChange') val: number = 0 |
@Builder |
结构复用 | @Builder Header() {} |
@Styles |
样式复用 | @Styles function card() {} |
@CustomDialog |
自定义弹窗 | @CustomDialog struct Alert {} |
10.5 生命周期速查
表格
| 方法 | 触发时机 | 用途 |
|---|---|---|
aboutToAppear() |
组件/页面创建 | 初始化数据、请求首屏 |
onPageShow() |
页面显示 | 刷新数据、恢复状态 |
onPageHide() |
页面隐藏 | 暂停计时器、保存草稿 |
aboutToDisappear() |
组件/页面销毁 | 清理资源、取消请求 |
onBackPress() |
返回键按下 | 拦截返回逻辑 |
onDidBuild() |
布局完成 | DOM 操作(API 12+) |
附录:开发常见问题
Q1:图片不显示?
-
网络图片:检查
module.json5中是否声明ohos.permission.INTERNET -
本地图片:确认图片放在
resources/base/media/目录,引用用$r('app.media.xxx') -
图片格式:推荐 PNG、JPG、SVG
Q2:状态修改后 UI 不刷新?
-
对象/数组属性变化:使用
@Observed+@ObjectLink -
数组整体赋值:
this.list = [...this.list](创建新引用) -
对象整体赋值:
this.obj = { ...this.obj, name: '新值' }
Q3:ForEach 报错或渲染异常?
-
必须提供第三个参数(key 函数):
ForEach(list, itemBuilder, (item) => item.id) -
确保 key 唯一且稳定
Q4:如何跳转页面并传参?
TypeScript
// 跳转
router.pushUrl({ url: 'pages/Detail', params: { id: 1 } })
// 接收
const params = router.getParams() as Record<string, object>
const id = params['id'] as number
Q5:如何获取屏幕宽高?
TypeScript
import { display } from '@kit.ArkUI'
const screenWidth = display.getDefaultDisplaySync().width
const screenHeight = display.getDefaultDisplaySync().height
文档版本:v1.0
最后更新:2026-07-25
适用 API:HarmonyOS API 12+
浙公网安备 33010602011771号