

子玥酱 (掘金 / 知乎 / CSDN / 简书 同名)
大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。
我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案,
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。
技术方向: 前端 / 跨端 / 小程序 / 移动端工程化 内容平台: 掘金、知乎、CSDN、简书 创作特点: 实战导向、源码拆解、少空谈多落地 **文章状态:**长期稳定更新,大量原创输出
我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在"API 怎么用",而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。
子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取 11 类前端进阶学习资源 (工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学"明白",也用"到位"
持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱
文章目录
-
- 引言
- [常见误解:文档模型 ≠ 文件读写](#常见误解:文档模型 ≠ 文件读写)
- 文档模型解决的不是"存不存",而是"活多久"
- 最小可用的文档模型,长什么样
- 文档管理器,是比页面更重要的中枢
- 多窗口问题,其实在这里自然消失了
- 页面要做的事情,反而变少了
- 为什么一定要"先"做文档模型
- 一个简单的自检点
- 总结
引言
在上一篇里我们已经达成一个共识:
在 HarmonyOS PC 场景下,
页面不再是核心,
文档才是。
但很多人在真正落地时,会立刻卡在一个现实问题上:
文档模型,
到底是一个类?
一套状态?
还是一个复杂框架?
如果你一开始就把"文档模型"想得太大,那这一步几乎一定会走偏。
常见误解:文档模型 ≠ 文件读写
很多人第一次听到"文档模型",下意识会想到:
- 打开文件
- 保存文件
- 解析内容
于是代码很快就会变成这样:
ts
class FileService {
load(path: string): string
save(path: string, content: string)
}
然后页面里这样用:
ts
onPageLoad(path) {
this.content = FileService.load(path)
}
onPageDestroy() {
FileService.save(path, this.content)
}
表面上看,你"有文件了",但这里其实还没有文档模型。
因为:
文件只是存储介质,
文档是运行时对象。
文档模型解决的不是"存不存",而是"活多久"
在 PC 应用里,真正麻烦的问题往往是这些:
- 什么时候该保存?
- 多个窗口是不是同一份内容?
- 页面销毁了,状态还在不在?
- 程序崩溃前,哪些改动是"已确认"的?
这些问题,文件 API 本身一个都解决不了。
所以文档模型的第一件事,不是 IO,而是:
明确谁拥有状态,以及状态的生命周期。
最小可用的文档模型,长什么样
一个合理的起点,其实非常克制:
ts
class Document {
id: string
content: string
dirty: boolean
applyChange(change) {
this.content = apply(this.content, change)
this.dirty = true
}
markSaved() {
this.dirty = false
}
}
注意这里的几个点:
- 文档是内存对象
- 状态集中在一个地方
- 是否需要保存,是文档自己的判断
页面不再持有核心状态:
ts
class EditorPage {
doc: Document
onLoad(docId) {
this.doc = DocumentManager.open(docId)
}
onInput(change) {
this.doc.applyChange(change)
}
}
这一刻开始,页面只是文档的使用者。
文档管理器,是比页面更重要的中枢
一旦有多个文档、多窗口,你一定会需要一个统一入口:
ts
class DocumentManager {
docs = new Map<string, Document>()
open(id) {
if (!this.docs.has(id)) {
this.docs.set(id, loadDocument(id))
}
return this.docs.get(id)
}
close(id) {
const doc = this.docs.get(id)
if (doc && doc.dirty) {
save(doc)
}
this.docs.delete(id)
}
}
这一步非常关键,因为它意味着:
文档的生死,不再由页面决定。
页面只是 attach / detach:
ts
onPageDestroy() {
DocumentManager.detach(this.doc.id)
}
什么时候真正 close,由文档管理器统一判断。
多窗口问题,其实在这里自然消失了
一旦文档是中心,多窗口几乎不用"设计"。
ts
const doc = DocumentManager.open(fileId)
openEditorWindow(doc)
openEditorWindow(doc)
两个窗口:
- 看到的是同一份 content
- 操作的是同一份状态
- 保存策略一致
你不需要:
- 复制状态
- 手动同步
- 写额外的桥接逻辑
因为模型本身已经对齐了 PC 使用方式。
页面要做的事情,反而变少了
在文档模型下,页面职责会被强制压缩:
ts
class EditorPage {
render(doc) {
draw(doc.content)
}
onUserInput(event) {
this.doc.applyChange(parse(event))
}
}
页面不再负责:
- 状态持久化
- 生命周期判断
- 资源回收策略
这不是"页面被削弱",而是职责终于对了。
为什么一定要"先"做文档模型
很多项目会想:
先把功能写出来,
后面再抽文档模型。
现实通常是:
- 状态已经散落在页面里
- 生命周期假设已经固化
- 多窗口是补丁
- 保存逻辑全是特判
这时候再引入文档模型,往往意味着一次大规模重构。
所以在 PC 场景下,正确的顺序应该是:
先定义文档如何存在,
再决定页面如何展示。
一个简单的自检点
如果你现在开始写 HarmonyOS PC 应用,可以问自己一句话:
页面关了,我的数据还应该在吗?
如果答案是"应该在",那这个数据就不该属于页面。
总结
在 HarmonyOS PC 应用里,
文档模型不是高级设计,
而是基础设施。
它解决的不是"写得优不优雅",而是:
- 状态是否集中
- 生命周期是否清晰
- 多窗口是否天然成立
- 长时间运行是否安全
当你一开始就把文档模型立住,后面的页面设计、窗口管理、状态同步,都会顺着来。