

子玥酱 (掘金 / 知乎 / CSDN / 简书 同名)
大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。
我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案,
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。
技术方向: 前端 / 跨端 / 小程序 / 移动端工程化 内容平台: 掘金、知乎、CSDN、简书 创作特点: 实战导向、源码拆解、少空谈多落地 **文章状态:**长期稳定更新,大量原创输出
我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在"API 怎么用",而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。
子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取 11 类前端进阶学习资源 (工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学"明白",也用"到位"
持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱
文章目录
-
- 引言
- [一、先划清三层:不要一开始就写 UI](#一、先划清三层:不要一开始就写 UI)
- 二、文档模型:不是数据,而是"状态载体"
-
- [错误理解:文档 = 一段内容](#错误理解:文档 = 一段内容)
- 正确做法:文档是一个"运行态对象"
- [三、Workspace:PC 世界的真正核心](#三、Workspace:PC 世界的真正核心)
-
- [Workspace 管什么?](#Workspace 管什么?)
- [一个最小可用的 Workspace 模型](#一个最小可用的 Workspace 模型)
- [四、Controller:把"操作流程"从 UI 中拔出来](#四、Controller:把“操作流程”从 UI 中拔出来)
-
- [错误:UI 直接 orchestrate 一切](#错误:UI 直接 orchestrate 一切)
- [正确:引入 WorkspaceController](#正确:引入 WorkspaceController)
- [五、UI:只是"视图 + 事件映射"](#五、UI:只是“视图 + 事件映射”)
- [六、为什么这套模型特别适合 PC?](#六、为什么这套模型特别适合 PC?)
- 七、一个现实世界的对照
- 总结
引言
PC 形态下,页面不是核心,文档才是。
那接下来真正的问题只有一个:
文档模型,到底该怎么设计,才能撑住多窗口、状态一致性和长期演进?
一、先划清三层:不要一开始就写 UI
一个标准的 HarmonyOS PC 文档模型,至少拆成三层:
┌──────────────────┐
│ UI(窗口 / 页面) │
└────────▲─────────┘
│ 订阅
┌────────┴─────────┐
│ 形态模型层 │ ← PC 专属
│ Workspace / Doc │
└────────▲─────────┘
│ 调用
┌────────┴─────────┐
│ 共用能力层 │
│ Repository / IO │
└──────────────────┘
注意顺序:
- UI 最后写
- 模型先稳定
- 能力层完全不关心形态
二、文档模型:不是数据,而是"状态载体"
错误理解:文档 = 一段内容
ts
interface Document {
id: string
content: string
}
这只能算存储结构,不算模型。
正确做法:文档是一个"运行态对象"
ts
// pc/model/DocumentModel.ts
export class DocumentModel {
readonly id: string
content: string
isDirty: boolean = false
lastFocusedAt: number = Date.now()
constructor(doc: Document) {
this.id = doc.id
this.content = doc.content
}
updateContent(newContent: string) {
this.content = newContent
this.isDirty = true
}
}
这里开始出现关键差异:
- 文档有 生命周期
- 文档知道自己"是否被修改"
- UI 不再自己维护脏状态
三、Workspace:PC 世界的真正核心
如果只记住一句话,那就是:
PC 应用里,Workspace 才是根对象。
Workspace 管什么?
- 当前打开了哪些文档
- 文档之间的关系
- 应用级行为(保存、关闭、恢复)
一个最小可用的 Workspace 模型
ts
// pc/model/WorkspaceModel.ts
export class WorkspaceModel {
private documents = new Map<string, DocumentModel>()
open(doc: Document) {
if (!this.documents.has(doc.id)) {
this.documents.set(doc.id, new DocumentModel(doc))
}
}
close(docId: string) {
this.documents.delete(docId)
}
list(): DocumentModel[] {
return [...this.documents.values()]
}
hasDirtyDocument(): boolean {
return this.list().some(d => d.isDirty)
}
}
这里有一个非常重要的工程价值:
Workspace 是唯一知道"当前应用状态"的地方。
UI 不需要猜,Service 不需要猜。
四、Controller:把"操作流程"从 UI 中拔出来
错误:UI 直接 orchestrate 一切
ts
onCloseApp() {
if (doc.isDirty) {
save()
}
exit()
}
这种代码一旦出现:
- 自动保存
- 批量关闭
- 崩溃恢复
全都没法统一。
正确:引入 WorkspaceController
ts
// pc/controller/WorkspaceController.ts
export class WorkspaceController {
constructor(
private workspace: WorkspaceModel,
private repo: DocumentRepository
) {}
saveAll() {
for (const doc of this.workspace.list()) {
if (doc.isDirty) {
this.repo.save({
id: doc.id,
content: doc.content
})
doc.isDirty = false
}
}
}
canExit(): boolean {
return !this.workspace.hasDirtyDocument()
}
}
现在:
- UI 只发意图
- Controller 统一流程
- 模型是状态来源
五、UI:只是"视图 + 事件映射"
到这一步,UI 会变得异常干净。
ts
// EditorWindow.ets
@State docModel!: DocumentModel
build() {
TextArea({
text: this.docModel.content
})
.onChange(v => this.docModel.updateContent(v))
}
你会发现:
- UI 不保存任何业务状态
- 没有保存逻辑
- 没有生命周期判断
UI 只是模型的一个投影。
六、为什么这套模型特别适合 PC?
因为它天然支持:
- 多窗口
- 多文档
- 后台保存
- 崩溃恢复
- 文档级权限控制
而且每一个能力都有清晰落点:
| 能力 | 归属 |
|---|---|
| 是否修改 | DocumentModel |
| 当前打开文档 | Workspace |
| 保存策略 | Controller |
| 展示 | UI |
七、一个现实世界的对照
如果你做过这些产品:
- VS Code
- Photoshop
- Keynote
- Xcode
你会发现它们无一不是文档模型驱动。HarmonyOS PC 只是终于走到了同一个阶段。
总结
HarmonyOS PC 应用的复杂度,不来自窗口多,而来自你有没有一个能承载复杂度的模型。
- 没有文档模型 → 状态迟早进 UI
- 没有 Workspace → 多窗口一定崩
- 没有 Controller → 行为一定散