你还在用“安卓思维”写鸿蒙?那你真的把 ArkTS & Stage 模型吃透了吗?

🏆本文收录于「滚雪球学SpringBoot」专栏(全网一个名),手把手带你零基础入门Spring Boot,从入门到就业,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8
🤔 摘要
先说句掏心窝子的话:我刚从“Android 老油条”转到鸿蒙那阵子,真是又兴奋又抓狂——兴奋是因为 ArkUI 写界面太爽了,抓狂是因为我脑子里那套“Activity/Fragment + XML”的肌肉记忆老来捣乱🤣。
所以这篇我不打算端着讲课(那多没劲呀),而是用一个**“能跑、能存、能请求网络、还能优雅扩展”的小项目**,把鸿蒙开发里最容易踩坑、也最值得掌握的核心串起来。读完你会有一种:**“欸?原来我之前是那样误会它的!”**的感觉😎
⚠️小说明(我得实诚一点):
你要求“全网查重率低于30% / 去AI化零AI味儿”,这类指标需要外部检测工具才能验证,我在这里没法替你做检测或保证具体比例。但我会尽最大努力用原创结构、原创表达、原创代码与工程化讲法来写,内容也会尽量像一位一线开发者写的技术笔记,而不是“模板化科普”😉
另外你说“根据你下面给的内容及大纲”,但你这一条消息里还没贴出来——我先给你一版可直接发布的完整长文,你把大纲/要点发我后,我再帮你精准改成你指定结构✅
📌目录(带点表情才像我嘛😄)
- 🧭 前言:我为什么劝你先丢掉“安卓思维”
- 🧠 鸿蒙应用到底“长啥样”:Stage 模型一眼看懂
- 🎨 ArkUI & 状态管理:写爽之前先写对
- 🧩 实战项目:做一个“番茄待办”小应用🍅
- 💾 数据持久化:Preferences 真香,但别乱用😅
- 🌐 网络请求:一把梭还是要讲武德🤺
- 🧰 工程化建议:结构、复用、可测试与性能小抠门
- 🕳️ 高频踩坑合集:我替你先踩了,别谢我🤣
- ✅ 结语:你现在敢说自己“会鸿蒙”了吗?
🧭 前言:我为什么劝你先丢掉“安卓思维”
你可能会说:“开发不都一样吗?UI + 业务 + 存储 + 网络。”
欸,这话对一半😄。另一半是——鸿蒙的体验感来自它的“系统级设计哲学”:分布式、原子化、统一 UI 声明式范式、Stage 模型的生命周期组织方式……这些东西你如果硬套安卓那套,就会出现一种很尴尬的局面:
**能写出来,但写得别扭;能跑起来,但跑得不优雅。**就像你穿西装去打球——不是不行,是你自己难受🤣
今天我就用一个小项目,带你从“会写”走到“写得像鸿蒙”,顺便给你几句我当时被锤醒的真理:
- **声明式 UI 不是‘换个写法’,而是‘换个脑回路’**🧠
- 状态是 UI 的老板,别跟老板对着干😤
- 工程结构不提前设计,后期你会跪着重构🙃
🧠 鸿蒙应用到底“长啥样”:Stage 模型一眼看懂
别被名词吓到哈😌。你只要记住一句话:
Stage 模型用“Ability + Window + 页面路由”来组织应用的生命周期与界面呈现。
在实际开发里,你最常接触的是:
- UIAbility:承载 UI 的能力(类似“主舞台”🎭)
- 页面(Page):ArkUI 声明式组件组成的界面
- 路由(router):页面跳转(别把它当 Android Intent 硬套就行😅)
你会发现它像是在告诉你:
“别把生命周期散落在各个角落,集中起来管理,页面就专心负责展示与交互。”
嗯,这话听着就很“系统工程”对吧?😎
🎨 ArkUI & 状态管理:写爽之前先写对
ArkUI 写界面确实爽,爽到你会忍不住一直写一直写,然后某天突然发现:
**“我改了数据,界面怎么没刷新?!”**😤
这时候别急着骂工具,先看看你是不是在跟状态管理较劲。
几个你必须形成肌肉记忆的关键词(我当初背它们背得像背单词🤣):
@State:组件内部状态,改它会触发 UI 刷新✨@Prop/@Link/@ObjectLink:父子组件数据传递与双向绑定(具体看场景)🔗- 不要直接 mutate(乱改)复杂对象,要让框架“感知到变化”👀
一句大白话:
你得让框架知道你变了,它才会帮你重绘。
(跟谈恋爱一样,你不说我咋知道你生气了🙃)
🧩 实战项目:做一个“番茄待办”小应用🍅
我们做个小而美的:Tomato Todo 🍅
功能很朴素,但每一项都很“练内功”:
- 新增待办 ✅
- 完成/取消完成 ☑️
- 本地持久化 💾
- 拉取一句网络鸡汤文案(当你写 bug 写到怀疑人生时用🤣)🌐
🧱 1)数据模型:别上来就一坨 Map(求你了🙏)
// common/model/TodoItem.ets
export class TodoItem {
id: string
title: string
done: boolean
createdAt: number
constructor(id: string, title: string, done: boolean = false, createdAt: number = Date.now()) {
this.id = id
this.title = title
this.done = done
this.createdAt = createdAt
}
}
你看,简单、明确、可扩展。以后你要加优先级、番茄钟时长、标签,都好加😎
🧠 2)一个轻量 Store:先别急着上“巨型架构”😅
// common/store/TodoStore.ets
import { TodoItem } from '../model/TodoItem'
export class TodoStore {
private _list: TodoItem[] = []
get list(): TodoItem[] {
return this._list
}
setList(next: TodoItem[]) {
this._list = next
}
add(title: string) {
const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`
this._list = [new TodoItem(id, title), ...this._list]
}
toggle(id: string) {
this._list = this._list.map(it => it.id === id ? new TodoItem(it.id, it.title, !it.done, it.createdAt) : it)
}
remove(id: string) {
this._list = this._list.filter(it => it.id !== id)
}
}
这里我故意用了“返回新数组”的方式更新数据:
一是清晰,二是利于 UI 框架感知变化。
(别问我怎么知道的,我以前原地 mutate 被坑过…😭)
🎨 3)主页面:ArkUI 写起来就是“顺滑”✨
// entry/src/main/ets/pages/Index.ets
import { TodoStore } from '../../common/store/TodoStore'
import { TodoItem } from '../../common/model/TodoItem'
import { TodoPersistence } from '../../common/storage/TodoPersistence'
import { QuoteService } from '../../common/net/QuoteService'
@Entry
@Component
struct Index {
@State private inputText: string = ''
@State private quote: string = '🍅 今天也要稳稳地写代码~'
@State private store: TodoStore = new TodoStore()
private persistence: TodoPersistence = new TodoPersistence('TomatoTodo')
private quoteService: QuoteService = new QuoteService()
aboutToAppear() {
// 进来先读本地数据
this.loadFromLocal()
// 顺手拉一句网络文案
this.loadQuote()
}
private async loadFromLocal() {
const list: TodoItem[] = await this.persistence.load()
this.store.setList(list)
// 触发刷新:store 变了
this.store = this.store
}
private async saveToLocal() {
await this.persistence.save(this.store.list)
}
private async loadQuote() {
const text = await this.quoteService.fetchOne()
if (text) this.quote = `💬 ${text}`
}
private addTodo() {
const title = this.inputText.trim()
if (!title) {
promptAction.showToast({ message: '😅 你倒是写点东西再点“添加”呀~' })
return
}
this.store.add(title)
this.inputText = ''
this.saveToLocal()
}
private toggleTodo(id: string) {
this.store.toggle(id)
this.saveToLocal()
}
private removeTodo(id: string) {
this.store.remove(id)
this.saveToLocal()
}
build() {
Column() {
Text('🍅 Tomato Todo')
.fontSize(26)
.fontWeight(FontWeight.Bold)
.margin({ top: 18, bottom: 6 })
Text(this.quote)
.fontSize(14)
.opacity(0.8)
.margin({ bottom: 12 })
Row() {
TextInput({ text: this.inputText, placeholder: '📝 写下一个待办,比如:把bug按在地上摩擦' })
.onChange((v: string) => this.inputText = v)
.layoutWeight(1)
.margin({ right: 8 })
Button('➕ 添加')
.onClick(() => this.addTodo())
}
.padding(12)
Divider().margin({ bottom: 6 })
List() {
ForEach(this.store.list, (item: TodoItem) => {
ListItem() {
Row() {
Checkbox({ name: item.id, group: 'todo', checked: item.done })
.onChange(() => this.toggleTodo(item.id))
.margin({ right: 10 })
Column() {
Text(item.title)
.fontSize(16)
.decoration({ type: item.done ? TextDecorationType.LineThrough : TextDecorationType.None })
Text(`⏱ ${new Date(item.createdAt).toLocaleString()}`)
.fontSize(12)
.opacity(0.6)
}
.layoutWeight(1)
Button('🗑️')
.onClick(() => this.removeTodo(item.id))
.margin({ left: 10 })
}
.padding(12)
}
}, (item: TodoItem) => item.id)
}
.height('70%')
}
.width('100%')
}
}
看到没?界面就是“数据长什么样,它就长什么样”。
这就是声明式 UI 的甜点:**可读、可维护、能组合。**😋
💾 数据持久化:Preferences 真香,但别乱用😅
轻量数据(比如 todo 列表这种不太大、不太复杂的)用 Preferences 很舒服。
但你千万别拿它当数据库使劲塞——等你塞到几百 KB、几 MB,你会开始怀疑人生🤣
// common/storage/TodoPersistence.ets
import preferences from '@ohos.data.preferences'
import { TodoItem } from '../model/TodoItem'
export class TodoPersistence {
private name: string
private key: string = 'todo_list'
constructor(name: string) {
this.name = name
}
async save(list: TodoItem[]) {
const pref = await preferences.getPreferences(getContext(), this.name)
await pref.put(this.key, JSON.stringify(list))
await pref.flush()
}
async load(): Promise<TodoItem[]> {
const pref = await preferences.getPreferences(getContext(), this.name)
const raw = await pref.get(this.key, '[]') as string
try {
const arr = JSON.parse(raw) as any[]
return arr.map(it => new TodoItem(it.id, it.title, it.done, it.createdAt))
} catch (e) {
return []
}
}
}
工程建议(真心话):
- 只存必要字段,别把一堆临时 UI 状态也存进去😵
- JSON 存储记得考虑版本升级(字段新增/改名)🧯
🌐 网络请求:一把梭还是要讲武德🤺
来,咱做个“鸡汤文案”接口拉取(你也可以换成你自己的后端)。这里给你一个通用写法:失败兜底、超时、返回空值不炸。
// common/net/QuoteService.ets
import http from '@ohos.net.http'
export class QuoteService {
async fetchOne(): Promise<string> {
const httpRequest = http.createHttp()
try {
const res = await httpRequest.request(
// 你可以替换为你自己的接口
'https://v1.hitokoto.cn/?c=i&encode=text',
{
method: http.RequestMethod.GET,
connectTimeout: 4000,
readTimeout: 4000
}
)
if (res.responseCode >= 200 && res.responseCode < 300) {
const text = (res.result as string)?.trim()
return text || '写代码累了?喝口水再继续呀💧'
}
return '网络有点傲娇,先不理我🙃'
} catch (e) {
return '网络翻车了,但你别翻车😏'
} finally {
httpRequest.destroy()
}
}
}
我特别想强调一句:
请求对象用完就 destroy(),别让资源泄漏像小强一样阴魂不散🪳
(是的,我就是被它折磨过的人😭)
🧰 工程化建议:结构、复用、可测试与性能小抠门
写鸿蒙写到后面,拼的不是“谁 API 背得多”,而是:
**谁的项目能活得久、迭代不崩、功能加得快。**😎
🧩 推荐的目录组织(别嫌我啰嗦,我是为你好😤)
你可以按这个拆:
common/model:纯数据结构common/store:业务状态/逻辑common/storage:持久化common/net:网络访问pages:页面(只做展示 + 调用 store)
⚡ 性能小抠门(真香)
- List 大数据别乱 ForEach + 大对象;给稳定 key✅
- 状态别“全局一锅炖”;局部状态局部管✅
- 避免频繁 JSON stringify/parse;必要时做节流⏳
🕳️ 高频踩坑合集:我替你先踩了,别谢我🤣
这段是“过来人碎碎念”,但句句都是真金白银换来的😭
-
改了对象属性 UI 不刷新
大概率你在原地 mutate,框架没感知到变化。
解决思路:用新对象/新数组替换,或使用合适的可观测机制👀 -
路由跳转后返回状态丢了
你把临时状态放错地方了。页面销毁就没了。
解决思路:该提升到 store 的提升到 store;该持久化的持久化💾 -
网络请求不销毁,越跑越慢
记得 destroy,不然资源就像欠债一样越滚越多💸 -
工程越写越像一坨意大利面🍝
别把业务逻辑写在 UI 里。短期你会爽,长期你会哭😭
✅ 结语:你现在敢说自己“会鸿蒙”了吗?
如果你看完能回答这三个问题,那我就敢拍胸脯说:你真的入门了😎
- **我能解释清楚 Stage 模型把生命周期管理带来了什么好处吗?**🤔
- **我能用“状态驱动 UI”而不是“命令式更新 UI”写页面吗?**😏
- **我能把项目拆成可维护结构,而不是把页面写成“上古神殿”吗?**🤣
🧧福利赠与你🧧
无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」专栏(全网一个名),bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。
最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。
同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板、技术文章Markdown文档等海量资料。
✨️ Who am I?
我是bug菌(全网一个名),CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云多年度十佳博主/价值贡献奖,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;更多精彩福利点击这里;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿。

-End-
- 点赞
- 收藏
- 关注作者
评论(0)