昨天深圳湾的海风带着些许咸味,我走进 1024 开发者沙龙的会场。作为一名全网 4w + 粉丝的技术博主,但说实话,其实我还是一个刚接触鸿蒙不到三个月的 "小白",能和这么多技术大牛坐在一起探讨 HarmonyOS NEXT 的 "深水区",心里既兴奋又忐忑。但当讲师在台上演示代码的那一刻,我突然意识到 —— 鸿蒙开发,其实没有想象中那么遥不可及。
回到家已经是晚上九点,脑子里还在回放沙龙上的技术分享。
趁着这股热乎劲儿,我想把这段时间从零开始学习鸿蒙的经历,以及今天的参会收获整理出来。这不仅是对自己学习历程的复盘,也希望能给同样在摸索的朋友们一些参考。
1 ArkTS: 重新认识 TypeScript 的 "近亲"
刚开始接触 HarmonyOS 开发时,最让我头疼的就是 ArkTS。虽然官方说它是 TypeScript 的超集,但真正上手后发现,这个 “超集” 可不简单。
在沙龙现场,一位华为的技术专家用了个很贴切的比喻:“如果说 TypeScript 是一辆普通汽车,那 ArkTS 就是在这辆车上装了涡轮增压、主动悬架和智能驾驶系统。” 这话说得我当时就笑了,但细想确实有道理 ——ArkTS 不仅继承了 TypeScript 的类型系统,还针对鸿蒙的声明式 UI 开发做了深度优化。
2 声明式 UI 的第一步
记得我写的第一个鸿蒙应用,就是经典的 "Hello World"。但和传统开发不同,鸿蒙的声明式 UI 让我眼前一亮。
简单的代码样式如下:
@Entry
@Component
struct HelloWorld {
@State message: string = '你好,HarmonyOS!'
build() {
Column() {
Text(this.message)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Blue)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
这段代码的逻辑清晰得让人舒服 ——@Entry 标记入口组件,@Component 定义组件,@State 管理状态。build () 方法里用链式调用描述 UI 结构,读起来就像在说 “我要一个占满屏幕的纵向容器,里面有段蓝色粗体文字,居中显示”。
运行这段代码,你会看到手机屏幕中央显示着 “你好,HarmonyOS!”。
简单,但这种声明式的写法却蕴含着强大的潜力。
温馨提示:@State 装饰的变量发生变化时,UI 会自动刷新。这点特别重要,初学者经常忘记加这个装饰器,导致界面更新不及时。
3 状态管理的智慧
在沙龙上,认识一位大佬,他和我讲了状态管理。他说很多开发者刚入门时,分不清 @State、@Prop、@Link 的区别。我深有同感,最初写代码时就经常用错。
后来我总结了个口诀:
(1)@State 是 "私有财产",只有自己能改。
(2)@Prop`是" 单向传递 ",父组件给子组件,子组件只能看不能改。
(3)@Link 是 "双向绑定",父子组件都能改,实时同步。
举个实际例子,做一个计数器应用:
@Entry
@Component
struct CounterApp {
@State count: number = 0
build() {
Column({ space: 20 }) {
Text(`当前计数: ${this.count}`)
.fontSize(32)
CounterButton({ count: $count })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
@Component
struct CounterButton {
@Link count: number
build() {
Row({ space: 10 }) {
Button('-')
.onClick(() => {
this.count--
})
Button('+')
.onClick(() => {
this.count++
})
}
}
}
这个例子中的父组件 CounterApp 通过 @State 管理计数,子组件 CounterButton 通过 @Link 与父组件双向绑定。点击加减按钮,计数会实时更新显示。这种组件间通信的方式,在实际项目中特别常用。
4 CodeGenie:AI 助力开发的惊喜体验
在 DevEco Studio 中,我使用 CodeGenie 来输入:" 创建一个音乐播放器界面,包含封面、歌名、进度条和播放按钮 "。
CodeGenie 思考了几秒,居然真的给我生成了代码:
@Component
export struct MusicPlayer {
@State songName: string = '星辰大海'
@State artist: string = '未知歌手'
@State isPlaying: boolean = false
@State progress: number = 30
build() {
Column({ space: 20 }) {
// 封面
Image($r('app.media.album_cover'))
.width(200)
.height(200)
.borderRadius(10)
// 歌曲信息
Column({ space: 5 }) {
Text(this.songName)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(this.artist)
.fontSize(14)
.fontColor('#999999')
}
// 进度条
Row({ space: 10 }) {
Text('01:23')
.fontSize(12)
Slider({
value: this.progress,
min: 0,
max: 100
})
.width('60%')
.onChange((value: number) => {
this.progress = value
})
Text('04:56')
.fontSize(12)
}
.width('90%')
// 播放按钮
Button(this.isPlaying ? '暂停' : '播放')
.fontSize(16)
.width(120)
.onClick(() => {
this.isPlaying = !this.isPlaying
})
}
.width('100%')
.padding(20)
}
}
虽然生成的代码还需要调整细节,比如图片资源路径、时间格式化等,但整体框架已经搭好了。这比我自己从零写要快太多。更重要的是,通过阅读 CodeGenie 生成的代码,我学到了很多布局技巧,比如 Column 和 Row 的嵌套使用、Slider 组件的属性设置等。
CodeGenie 生成的代码是起点而非终点,需要根据实际需求进行调整和优化。但它确实能大幅降低学习成本,特别适合初学者快速上手。
5 云开发:告别后端配置的烦恼
在沙龙的云开发专场,大佬给我演示了如何用 AGC (AppGallery Connect) 快速搭建后端服务,鸿蒙的云开发让这一切变得简单。
我尝试做了个简单的待办事项应用:
(1)在 AGC 控制台创建云数据库,定义了一个 TodoItem 对象类:
export class TodoItem {
id?: string
title: string = ''
completed: boolean = false
createTime: number = 0
}
(2)在应用中调用云数据库 API:
import cloudDatabase from '@hw-agconnect/database'
@Entry
@Component
struct TodoApp {
@State todoList: TodoItem[] = []
@State inputText: string = ''
private dbZone?: cloudDatabase.CloudDBZone
async aboutToAppear() {
// 初始化云数据库
this.dbZone = await cloudDatabase.getCloudDBZone('TodoZone')
this.loadTodos()
}
async loadTodos() {
if (!this.dbZone) return
const query = cloudDatabase.query(TodoItem)
const result = await this.dbZone.executeQuery(query)
this.todoList = result.getSnapshotObjects()
}
async addTodo() {
if (!this.dbZone || !this.inputText) return
const newTodo: TodoItem = {
title: this.inputText,
completed: false,
createTime: Date.now()
}
await this.dbZone.executeUpsert(newTodo)
this.inputText = ''
this.loadTodos()
}
build() {
Column({ space: 15 }) {
// 输入框
Row({ space: 10 }) {
TextInput({ placeholder: '添加待办事项' })
.layoutWeight(1)
.onChange((value: string) => {
this.inputText = value
})
Button('添加')
.onClick(() => this.addTodo())
}
.width('90%')
.padding({ top: 20 })
// 待办列表
List({ space: 10 }) {
ForEach(this.todoList, (item: TodoItem) => {
ListItem() {
Row({ space: 10 }) {
Checkbox()
.select(item.completed)
.onChange((value: boolean) => {
item.completed = value
this.dbZone?.executeUpsert(item)
})
Text(item.title)
.fontSize(16)
.decoration({
type: item.completed ? TextDecorationType.LineThrough : TextDecorationType.None
})
}
.width('100%')
.padding(10)
.backgroundColor('#f5f5f5')
.borderRadius(8)
}
})
}
.width('90%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
}
}
这段代码实现了完整的增查改功能,数据自动同步到云端。用户在不同设备上登录,都能看到相同的待办列表。最神奇的是,我没写一行后端代码,连服务器都不用自己搭建。
运行效果就是一个简洁的待办清单:顶部输入框可以添加事项,下方列表展示所有待办,点击复选框标记完成状态,完成的事项会显示删除线。所有操作实时同步到云端。
6 预加载:提升用户体验的关键
大佬还和我提到,很多开发者容易忽视应用启动速度,导致用户体验不佳。预加载技术可以显著改善这个问题。
我在自己的应用中实现了页面预加载。
思路是在应用启动时,提前加载常用页面的资源:
import router from '@ohos.router'
@Entry
@Component
struct MainPage {
async aboutToAppear() {
// 预加载详情页
router.preloadPage({
url: 'pages/DetailPage',
mode: router.RouterMode.Standard
})
}
build() {
Column() {
List() {
ForEach([1, 2, 3, 4, 5], (item: number) => {
ListItem() {
Text(`新闻标题 ${item}`)
.fontSize(18)
.padding(15)
.onClick(() => {
router.pushUrl({
url: 'pages/DetailPage',
params: { id: item }
})
})
}
})
}
}
}
}
预加载后,点击列表项跳转详情页的速度明显变快。用户几乎感觉不到加载过程,体验提升了一个档次。
预加载会占用内存,不要一次性加载太多页面。建议只预加载高频访问的 1-2 个页面。
7 AppLinking: 深度链接的魔力
AppLinking 是我在沙龙上从大佬身上学到的另一个实用技术,它允许通过 URL 直接打开应用的特定页面,这在营销推广、内容分享场景下特别有用。
下面我来展示具体的步骤:
(1)在 module.json5 中配置:
{
"abilities": [
{
"name": "EntryAbility",
"skills": [
{
"actions": ["ohos.want.action.viewData"],
"uris": [
{
"scheme": "myapp",
"host": "detail",
"path": "/news"
}
]
}
]
}
]
}
(2)在代码中处理链接跳转:
import Want from '@ohos.app.ability.Want'
export default class EntryAbility extends UIAbility {
onCreate(want: Want) {
// 解析深度链接参数
if (want.uri) {
const params = this.parseUri(want.uri)
// 根据参数跳转到相应页面
if (params.id) {
router.pushUrl({
url: 'pages/DetailPage',
params: { id: params.id }
})
}
}
}
parseUri(uri: string): Record<string, string> {
const params: Record<string, string> = {}
const query = uri.split('?')[1]
if (query) {
query.split('&').forEach(item => {
const [key, value] = item.split('=')
params[key] = decodeURIComponent(value)
})
}
return params
}
}
配置完成后,用户点击 myapp://detail/news?id=123 这样的链接,就能直接跳转到应用的新闻详情页,id 参数也会正确传递。这种无缝跳转的体验,对提高用户留存率很有帮助。
8 从沙龙到实践的思考
从深圳湾沙龙回来,我的脑子里一直在回想那些技术分享。讲师们以及结识的大佬不仅讲解了 HarmonyOS NEXT 的新特性,更重要的是分享了实际开发中的经验和踩过的坑。
大佬和我说的一句话让我印象深刻:" 鸿蒙开发的门槛确实不低,但一旦跨过这道门槛,你会发现这是一个充满可能的新世界。" 这三个月的学习历程,我深有体会。
刚开始接触 ArkTS 时,我觉得语法太复杂,各种装饰器看得眼花缭乱。但当我真正理解了声明式 UI 的设计理念后,反而觉得这种方式更符合直觉。写代码就像搭积木,每个组件职责清晰,组合起来就能构建复杂的界面。
云开发更是解决了我的大难题。以前做个人项目,光是搭建后端环境就要花好几天。现在有了 AGC,数据库、存储、认证这些服务开箱即用,让我能把更多精力放在业务逻辑和用户体验上。
回顾这段学习历程,我总结了一些经验,希望能帮到刚入门的朋友:
第一阶段:熟悉基础
从 "鸿蒙第一课" 开始,系统学习 ArkTS 语法和声明式 UI。不要急着做复杂项目,先把官方的示例代码都跑一遍,理解每个装饰器、每个组件的作用。这个阶段可能需要 2-3 周。
第二阶段:动手实践
选一个简单的项目实战,比如待办清单、天气应用、新闻阅读器。从零开始搭建,遇到问题先查文档,实在解决不了再去论坛提问。这个过程会踩很多坑,但正是通过踩坑才能真正理解技术细节。这个阶段建议持续 1-2 个月。
第三阶段:深入优化
当基本功能实现后,开始关注性能、用户体验这些细节。学习预加载、APMS 监控、云测试等进阶技术。参加线下沙龙、开发者社区交流,吸收别人的经验。这个阶段是个持续迭代的过程。
学习编程没有捷径,但有方法。多写代码,多看别人的代码,多思考为什么这样写。遇到问题不要怕,每解决一个问题,你的技能就提升一分。
从深圳湾沙龙走出来,我拍了张海边的照片。夕阳把海面染成了金色,几只海鸥在空中盘旋。那一刻我突然想到,学习鸿蒙开发就像在这片海上航行 —— 刚开始可能会迷失方向,可能会遇到风浪,但只要坚持前行,总能到达彼岸。
这篇文章记录了我这三个月的学习历程,也融入了昨天沙龙的收获。如果你也是鸿蒙的初学者,希望这些经验能给你一些帮助。如果你已经在鸿蒙开发的路上走了一段时间,欢迎分享你的故事。
技术的世界很大,鸿蒙的舞台很广。2025 年的这个秋天,让我们一起码上 "鸿" 图,在 HarmonyOS NEXT 的深水区里探索更多可能。
星光不负赶路人,代码终将成就梦想。