Stage 工程落地清单:从 Demo 到可维护 HarmonyOS 应用
前两篇我们已经把 Stage 工程目录、首页加载、资源管理和页面跳转讲清楚了。从这一篇开始,重点放到 Stage 模型本身:哪些代码应该放在 AbilityStage,哪些代码应该放在 UIAbility,哪些场景应该使用 ExtensionAbility,后台任务又应该怎样设计。
本文的目标很明确:让读者看完后能把概念落到工程里,而不是只记住几个类名。

1. 本章先解决什么问题
学完 Stage 模型后,真正落地项目要解决目录、职责、服务、排错和发布前检查。本文给出一套可执行清单。
如果你正在写 HarmonyOS 应用,可以先带着三个问题读本文:
- 这段代码应该放在入口、页面、服务层还是扩展能力里?
- 出问题时应该先查配置、生命周期、页面路由还是后台任务?
- 这个能力是否真的需要后台运行,还是可以交给前台页面或系统调度?
补充流程图:
#mermaid-svg-Lt3MCxSxqRKDjVkZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .error-icon{fill:#552222;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .marker.cross{stroke:#333333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Lt3MCxSxqRKDjVkZ p{margin:0;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .cluster-label text{fill:#333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .cluster-label span{color:#333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .cluster-label span p{background-color:transparent;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .label text,#mermaid-svg-Lt3MCxSxqRKDjVkZ span{fill:#333;color:#333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .node rect,#mermaid-svg-Lt3MCxSxqRKDjVkZ .node circle,#mermaid-svg-Lt3MCxSxqRKDjVkZ .node ellipse,#mermaid-svg-Lt3MCxSxqRKDjVkZ .node polygon,#mermaid-svg-Lt3MCxSxqRKDjVkZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .rough-node .label text,#mermaid-svg-Lt3MCxSxqRKDjVkZ .node .label text,#mermaid-svg-Lt3MCxSxqRKDjVkZ .image-shape .label,#mermaid-svg-Lt3MCxSxqRKDjVkZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .rough-node .label,#mermaid-svg-Lt3MCxSxqRKDjVkZ .node .label,#mermaid-svg-Lt3MCxSxqRKDjVkZ .image-shape .label,#mermaid-svg-Lt3MCxSxqRKDjVkZ .icon-shape .label{text-align:center;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .node.clickable{cursor:pointer;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .arrowheadPath{fill:#333333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Lt3MCxSxqRKDjVkZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lt3MCxSxqRKDjVkZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Lt3MCxSxqRKDjVkZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .cluster text{fill:#333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .cluster span{color:#333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Lt3MCxSxqRKDjVkZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .icon-shape,#mermaid-svg-Lt3MCxSxqRKDjVkZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .icon-shape p,#mermaid-svg-Lt3MCxSxqRKDjVkZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .icon-shape .label rect,#mermaid-svg-Lt3MCxSxqRKDjVkZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lt3MCxSxqRKDjVkZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Lt3MCxSxqRKDjVkZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Lt3MCxSxqRKDjVkZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:本章先解决什么问题
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
export interface 本章先解决什么问题StageNode {
name: string;
owner: 'AbilityStage' | 'UIAbility' | 'ExtensionAbility' | 'Page' | 'Service';
responsibility: string;
}
export const 本章先解决什么问题Node: 本章先解决什么问题StageNode = {
name: '本章先解决什么问题',
owner: 'Service',
responsibility: '把本小节的概念落到可复用工程代码中'
};
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把小节概念转成一个职责节点。写 Stage 工程时,先明确 owner,再决定代码放在哪个目录,能减少职责混乱。
2. 为什么需要工程清单
Demo 能跑不代表项目可维护。真实项目会持续增加页面、服务、资源、权限和后台场景,没有清单很容易越写越乱。
补充流程图:
#mermaid-svg-wdU6s0khMhRQfsqf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wdU6s0khMhRQfsqf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wdU6s0khMhRQfsqf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wdU6s0khMhRQfsqf .error-icon{fill:#552222;}#mermaid-svg-wdU6s0khMhRQfsqf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wdU6s0khMhRQfsqf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wdU6s0khMhRQfsqf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wdU6s0khMhRQfsqf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wdU6s0khMhRQfsqf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wdU6s0khMhRQfsqf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wdU6s0khMhRQfsqf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wdU6s0khMhRQfsqf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wdU6s0khMhRQfsqf .marker.cross{stroke:#333333;}#mermaid-svg-wdU6s0khMhRQfsqf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wdU6s0khMhRQfsqf p{margin:0;}#mermaid-svg-wdU6s0khMhRQfsqf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wdU6s0khMhRQfsqf .cluster-label text{fill:#333;}#mermaid-svg-wdU6s0khMhRQfsqf .cluster-label span{color:#333;}#mermaid-svg-wdU6s0khMhRQfsqf .cluster-label span p{background-color:transparent;}#mermaid-svg-wdU6s0khMhRQfsqf .label text,#mermaid-svg-wdU6s0khMhRQfsqf span{fill:#333;color:#333;}#mermaid-svg-wdU6s0khMhRQfsqf .node rect,#mermaid-svg-wdU6s0khMhRQfsqf .node circle,#mermaid-svg-wdU6s0khMhRQfsqf .node ellipse,#mermaid-svg-wdU6s0khMhRQfsqf .node polygon,#mermaid-svg-wdU6s0khMhRQfsqf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wdU6s0khMhRQfsqf .rough-node .label text,#mermaid-svg-wdU6s0khMhRQfsqf .node .label text,#mermaid-svg-wdU6s0khMhRQfsqf .image-shape .label,#mermaid-svg-wdU6s0khMhRQfsqf .icon-shape .label{text-anchor:middle;}#mermaid-svg-wdU6s0khMhRQfsqf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wdU6s0khMhRQfsqf .rough-node .label,#mermaid-svg-wdU6s0khMhRQfsqf .node .label,#mermaid-svg-wdU6s0khMhRQfsqf .image-shape .label,#mermaid-svg-wdU6s0khMhRQfsqf .icon-shape .label{text-align:center;}#mermaid-svg-wdU6s0khMhRQfsqf .node.clickable{cursor:pointer;}#mermaid-svg-wdU6s0khMhRQfsqf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wdU6s0khMhRQfsqf .arrowheadPath{fill:#333333;}#mermaid-svg-wdU6s0khMhRQfsqf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wdU6s0khMhRQfsqf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wdU6s0khMhRQfsqf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wdU6s0khMhRQfsqf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wdU6s0khMhRQfsqf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wdU6s0khMhRQfsqf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wdU6s0khMhRQfsqf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wdU6s0khMhRQfsqf .cluster text{fill:#333;}#mermaid-svg-wdU6s0khMhRQfsqf .cluster span{color:#333;}#mermaid-svg-wdU6s0khMhRQfsqf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wdU6s0khMhRQfsqf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wdU6s0khMhRQfsqf rect.text{fill:none;stroke-width:0;}#mermaid-svg-wdU6s0khMhRQfsqf .icon-shape,#mermaid-svg-wdU6s0khMhRQfsqf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wdU6s0khMhRQfsqf .icon-shape p,#mermaid-svg-wdU6s0khMhRQfsqf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wdU6s0khMhRQfsqf .icon-shape .label rect,#mermaid-svg-wdU6s0khMhRQfsqf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wdU6s0khMhRQfsqf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wdU6s0khMhRQfsqf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wdU6s0khMhRQfsqf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:为什么需要工程清单
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
export interface 为什么需要工程清单StageNode {
name: string;
owner: 'AbilityStage' | 'UIAbility' | 'ExtensionAbility' | 'Page' | 'Service';
responsibility: string;
}
export const 为什么需要工程清单Node: 为什么需要工程清单StageNode = {
name: '为什么需要工程清单',
owner: 'Service',
responsibility: '把本小节的概念落到可复用工程代码中'
};
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把小节概念转成一个职责节点。写 Stage 工程时,先明确 owner,再决定代码放在哪个目录,能减少职责混乱。
3. 目录结构先稳定
页面放 pages,组件放 components,服务放 services,模型放 models,工具放 utils。结构稳定后,新功能才不会随手乱放。
补充流程图:
#mermaid-svg-YHdPfR1ixHoD3GMU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YHdPfR1ixHoD3GMU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YHdPfR1ixHoD3GMU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YHdPfR1ixHoD3GMU .error-icon{fill:#552222;}#mermaid-svg-YHdPfR1ixHoD3GMU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YHdPfR1ixHoD3GMU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YHdPfR1ixHoD3GMU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YHdPfR1ixHoD3GMU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YHdPfR1ixHoD3GMU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YHdPfR1ixHoD3GMU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YHdPfR1ixHoD3GMU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YHdPfR1ixHoD3GMU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YHdPfR1ixHoD3GMU .marker.cross{stroke:#333333;}#mermaid-svg-YHdPfR1ixHoD3GMU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YHdPfR1ixHoD3GMU p{margin:0;}#mermaid-svg-YHdPfR1ixHoD3GMU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YHdPfR1ixHoD3GMU .cluster-label text{fill:#333;}#mermaid-svg-YHdPfR1ixHoD3GMU .cluster-label span{color:#333;}#mermaid-svg-YHdPfR1ixHoD3GMU .cluster-label span p{background-color:transparent;}#mermaid-svg-YHdPfR1ixHoD3GMU .label text,#mermaid-svg-YHdPfR1ixHoD3GMU span{fill:#333;color:#333;}#mermaid-svg-YHdPfR1ixHoD3GMU .node rect,#mermaid-svg-YHdPfR1ixHoD3GMU .node circle,#mermaid-svg-YHdPfR1ixHoD3GMU .node ellipse,#mermaid-svg-YHdPfR1ixHoD3GMU .node polygon,#mermaid-svg-YHdPfR1ixHoD3GMU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YHdPfR1ixHoD3GMU .rough-node .label text,#mermaid-svg-YHdPfR1ixHoD3GMU .node .label text,#mermaid-svg-YHdPfR1ixHoD3GMU .image-shape .label,#mermaid-svg-YHdPfR1ixHoD3GMU .icon-shape .label{text-anchor:middle;}#mermaid-svg-YHdPfR1ixHoD3GMU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YHdPfR1ixHoD3GMU .rough-node .label,#mermaid-svg-YHdPfR1ixHoD3GMU .node .label,#mermaid-svg-YHdPfR1ixHoD3GMU .image-shape .label,#mermaid-svg-YHdPfR1ixHoD3GMU .icon-shape .label{text-align:center;}#mermaid-svg-YHdPfR1ixHoD3GMU .node.clickable{cursor:pointer;}#mermaid-svg-YHdPfR1ixHoD3GMU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YHdPfR1ixHoD3GMU .arrowheadPath{fill:#333333;}#mermaid-svg-YHdPfR1ixHoD3GMU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YHdPfR1ixHoD3GMU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YHdPfR1ixHoD3GMU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YHdPfR1ixHoD3GMU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YHdPfR1ixHoD3GMU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YHdPfR1ixHoD3GMU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YHdPfR1ixHoD3GMU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YHdPfR1ixHoD3GMU .cluster text{fill:#333;}#mermaid-svg-YHdPfR1ixHoD3GMU .cluster span{color:#333;}#mermaid-svg-YHdPfR1ixHoD3GMU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YHdPfR1ixHoD3GMU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YHdPfR1ixHoD3GMU rect.text{fill:none;stroke-width:0;}#mermaid-svg-YHdPfR1ixHoD3GMU .icon-shape,#mermaid-svg-YHdPfR1ixHoD3GMU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YHdPfR1ixHoD3GMU .icon-shape p,#mermaid-svg-YHdPfR1ixHoD3GMU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YHdPfR1ixHoD3GMU .icon-shape .label rect,#mermaid-svg-YHdPfR1ixHoD3GMU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YHdPfR1ixHoD3GMU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YHdPfR1ixHoD3GMU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YHdPfR1ixHoD3GMU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:目录结构先稳定
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
import { common } from '@kit.AbilityKit';
export class 目录结构先稳定ContextHelper {
static getWorkPath(context: common.UIAbilityContext): string {
return `${context.filesDir}/目录结构先稳定.json`;
}
static getCachePath(context: common.UIAbilityContext): string {
return `${context.cacheDir}/目录结构先稳定.cache`;
}
}
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把 Context 相关目录访问封装成工具方法。页面只拿结果,不直接到处拼路径,可以降低路径写错和目录混用的风险。
4. 入口边界要守住
AbilityStage 做模块初始化,UIAbility 做界面入口,ExtensionAbility 做系统扩展,ArkUI 页面做展示和交互。
补充流程图:
#mermaid-svg-eOIgsYM38uSQAsZk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eOIgsYM38uSQAsZk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eOIgsYM38uSQAsZk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eOIgsYM38uSQAsZk .error-icon{fill:#552222;}#mermaid-svg-eOIgsYM38uSQAsZk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eOIgsYM38uSQAsZk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eOIgsYM38uSQAsZk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eOIgsYM38uSQAsZk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eOIgsYM38uSQAsZk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eOIgsYM38uSQAsZk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eOIgsYM38uSQAsZk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eOIgsYM38uSQAsZk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eOIgsYM38uSQAsZk .marker.cross{stroke:#333333;}#mermaid-svg-eOIgsYM38uSQAsZk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eOIgsYM38uSQAsZk p{margin:0;}#mermaid-svg-eOIgsYM38uSQAsZk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eOIgsYM38uSQAsZk .cluster-label text{fill:#333;}#mermaid-svg-eOIgsYM38uSQAsZk .cluster-label span{color:#333;}#mermaid-svg-eOIgsYM38uSQAsZk .cluster-label span p{background-color:transparent;}#mermaid-svg-eOIgsYM38uSQAsZk .label text,#mermaid-svg-eOIgsYM38uSQAsZk span{fill:#333;color:#333;}#mermaid-svg-eOIgsYM38uSQAsZk .node rect,#mermaid-svg-eOIgsYM38uSQAsZk .node circle,#mermaid-svg-eOIgsYM38uSQAsZk .node ellipse,#mermaid-svg-eOIgsYM38uSQAsZk .node polygon,#mermaid-svg-eOIgsYM38uSQAsZk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eOIgsYM38uSQAsZk .rough-node .label text,#mermaid-svg-eOIgsYM38uSQAsZk .node .label text,#mermaid-svg-eOIgsYM38uSQAsZk .image-shape .label,#mermaid-svg-eOIgsYM38uSQAsZk .icon-shape .label{text-anchor:middle;}#mermaid-svg-eOIgsYM38uSQAsZk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eOIgsYM38uSQAsZk .rough-node .label,#mermaid-svg-eOIgsYM38uSQAsZk .node .label,#mermaid-svg-eOIgsYM38uSQAsZk .image-shape .label,#mermaid-svg-eOIgsYM38uSQAsZk .icon-shape .label{text-align:center;}#mermaid-svg-eOIgsYM38uSQAsZk .node.clickable{cursor:pointer;}#mermaid-svg-eOIgsYM38uSQAsZk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eOIgsYM38uSQAsZk .arrowheadPath{fill:#333333;}#mermaid-svg-eOIgsYM38uSQAsZk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eOIgsYM38uSQAsZk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eOIgsYM38uSQAsZk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eOIgsYM38uSQAsZk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eOIgsYM38uSQAsZk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eOIgsYM38uSQAsZk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eOIgsYM38uSQAsZk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eOIgsYM38uSQAsZk .cluster text{fill:#333;}#mermaid-svg-eOIgsYM38uSQAsZk .cluster span{color:#333;}#mermaid-svg-eOIgsYM38uSQAsZk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eOIgsYM38uSQAsZk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eOIgsYM38uSQAsZk rect.text{fill:none;stroke-width:0;}#mermaid-svg-eOIgsYM38uSQAsZk .icon-shape,#mermaid-svg-eOIgsYM38uSQAsZk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eOIgsYM38uSQAsZk .icon-shape p,#mermaid-svg-eOIgsYM38uSQAsZk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eOIgsYM38uSQAsZk .icon-shape .label rect,#mermaid-svg-eOIgsYM38uSQAsZk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eOIgsYM38uSQAsZk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eOIgsYM38uSQAsZk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eOIgsYM38uSQAsZk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:入口边界要守住
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
export interface 入口边界要守住StageNode {
name: string;
owner: 'AbilityStage' | 'UIAbility' | 'ExtensionAbility' | 'Page' | 'Service';
responsibility: string;
}
export const 入口边界要守住Node: 入口边界要守住StageNode = {
name: '入口边界要守住',
owner: 'Service',
responsibility: '把本小节的概念落到可复用工程代码中'
};
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把小节概念转成一个职责节点。写 Stage 工程时,先明确 owner,再决定代码放在哪个目录,能减少职责混乱。
5. 服务层承担业务复用
网络、缓存、任务、配置、日志都应封装成服务,不要散落在页面事件里。
补充流程图:
#mermaid-svg-E2SgxlJl08yeVOuB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-E2SgxlJl08yeVOuB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-E2SgxlJl08yeVOuB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-E2SgxlJl08yeVOuB .error-icon{fill:#552222;}#mermaid-svg-E2SgxlJl08yeVOuB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-E2SgxlJl08yeVOuB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-E2SgxlJl08yeVOuB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-E2SgxlJl08yeVOuB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-E2SgxlJl08yeVOuB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-E2SgxlJl08yeVOuB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-E2SgxlJl08yeVOuB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-E2SgxlJl08yeVOuB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-E2SgxlJl08yeVOuB .marker.cross{stroke:#333333;}#mermaid-svg-E2SgxlJl08yeVOuB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-E2SgxlJl08yeVOuB p{margin:0;}#mermaid-svg-E2SgxlJl08yeVOuB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-E2SgxlJl08yeVOuB .cluster-label text{fill:#333;}#mermaid-svg-E2SgxlJl08yeVOuB .cluster-label span{color:#333;}#mermaid-svg-E2SgxlJl08yeVOuB .cluster-label span p{background-color:transparent;}#mermaid-svg-E2SgxlJl08yeVOuB .label text,#mermaid-svg-E2SgxlJl08yeVOuB span{fill:#333;color:#333;}#mermaid-svg-E2SgxlJl08yeVOuB .node rect,#mermaid-svg-E2SgxlJl08yeVOuB .node circle,#mermaid-svg-E2SgxlJl08yeVOuB .node ellipse,#mermaid-svg-E2SgxlJl08yeVOuB .node polygon,#mermaid-svg-E2SgxlJl08yeVOuB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-E2SgxlJl08yeVOuB .rough-node .label text,#mermaid-svg-E2SgxlJl08yeVOuB .node .label text,#mermaid-svg-E2SgxlJl08yeVOuB .image-shape .label,#mermaid-svg-E2SgxlJl08yeVOuB .icon-shape .label{text-anchor:middle;}#mermaid-svg-E2SgxlJl08yeVOuB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-E2SgxlJl08yeVOuB .rough-node .label,#mermaid-svg-E2SgxlJl08yeVOuB .node .label,#mermaid-svg-E2SgxlJl08yeVOuB .image-shape .label,#mermaid-svg-E2SgxlJl08yeVOuB .icon-shape .label{text-align:center;}#mermaid-svg-E2SgxlJl08yeVOuB .node.clickable{cursor:pointer;}#mermaid-svg-E2SgxlJl08yeVOuB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-E2SgxlJl08yeVOuB .arrowheadPath{fill:#333333;}#mermaid-svg-E2SgxlJl08yeVOuB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-E2SgxlJl08yeVOuB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-E2SgxlJl08yeVOuB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E2SgxlJl08yeVOuB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-E2SgxlJl08yeVOuB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E2SgxlJl08yeVOuB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-E2SgxlJl08yeVOuB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-E2SgxlJl08yeVOuB .cluster text{fill:#333;}#mermaid-svg-E2SgxlJl08yeVOuB .cluster span{color:#333;}#mermaid-svg-E2SgxlJl08yeVOuB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-E2SgxlJl08yeVOuB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-E2SgxlJl08yeVOuB rect.text{fill:none;stroke-width:0;}#mermaid-svg-E2SgxlJl08yeVOuB .icon-shape,#mermaid-svg-E2SgxlJl08yeVOuB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E2SgxlJl08yeVOuB .icon-shape p,#mermaid-svg-E2SgxlJl08yeVOuB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-E2SgxlJl08yeVOuB .icon-shape .label rect,#mermaid-svg-E2SgxlJl08yeVOuB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E2SgxlJl08yeVOuB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-E2SgxlJl08yeVOuB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-E2SgxlJl08yeVOuB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:服务层承担业务复用
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
interface ExtensionRequest {
type: 'service' | 'form' | 'share';
payload: Record<string, string>;
}
export class 服务层承担业务复用ExtensionRouter {
static route(request: ExtensionRequest): string {
if (request.type === 'service') {
return 'handle service request';
}
if (request.type === 'form') {
return 'update form content';
}
return 'handle share content';
}
}
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把不同扩展入口先统一成请求对象,再按 type 分发。这样可以避免把服务、卡片、分享逻辑全部写在一个生命周期回调里。
6. 排错要有固定路径
启动问题查 module.json5 和 EntryAbility,页面问题查 router 和 main_pages,后台问题查任务类型和系统限制。
补充流程图:
#mermaid-svg-v9Y5vCmxszf7JJoD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-v9Y5vCmxszf7JJoD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-v9Y5vCmxszf7JJoD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-v9Y5vCmxszf7JJoD .error-icon{fill:#552222;}#mermaid-svg-v9Y5vCmxszf7JJoD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-v9Y5vCmxszf7JJoD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-v9Y5vCmxszf7JJoD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-v9Y5vCmxszf7JJoD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-v9Y5vCmxszf7JJoD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-v9Y5vCmxszf7JJoD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-v9Y5vCmxszf7JJoD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-v9Y5vCmxszf7JJoD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-v9Y5vCmxszf7JJoD .marker.cross{stroke:#333333;}#mermaid-svg-v9Y5vCmxszf7JJoD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-v9Y5vCmxszf7JJoD p{margin:0;}#mermaid-svg-v9Y5vCmxszf7JJoD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-v9Y5vCmxszf7JJoD .cluster-label text{fill:#333;}#mermaid-svg-v9Y5vCmxszf7JJoD .cluster-label span{color:#333;}#mermaid-svg-v9Y5vCmxszf7JJoD .cluster-label span p{background-color:transparent;}#mermaid-svg-v9Y5vCmxszf7JJoD .label text,#mermaid-svg-v9Y5vCmxszf7JJoD span{fill:#333;color:#333;}#mermaid-svg-v9Y5vCmxszf7JJoD .node rect,#mermaid-svg-v9Y5vCmxszf7JJoD .node circle,#mermaid-svg-v9Y5vCmxszf7JJoD .node ellipse,#mermaid-svg-v9Y5vCmxszf7JJoD .node polygon,#mermaid-svg-v9Y5vCmxszf7JJoD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-v9Y5vCmxszf7JJoD .rough-node .label text,#mermaid-svg-v9Y5vCmxszf7JJoD .node .label text,#mermaid-svg-v9Y5vCmxszf7JJoD .image-shape .label,#mermaid-svg-v9Y5vCmxszf7JJoD .icon-shape .label{text-anchor:middle;}#mermaid-svg-v9Y5vCmxszf7JJoD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-v9Y5vCmxszf7JJoD .rough-node .label,#mermaid-svg-v9Y5vCmxszf7JJoD .node .label,#mermaid-svg-v9Y5vCmxszf7JJoD .image-shape .label,#mermaid-svg-v9Y5vCmxszf7JJoD .icon-shape .label{text-align:center;}#mermaid-svg-v9Y5vCmxszf7JJoD .node.clickable{cursor:pointer;}#mermaid-svg-v9Y5vCmxszf7JJoD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-v9Y5vCmxszf7JJoD .arrowheadPath{fill:#333333;}#mermaid-svg-v9Y5vCmxszf7JJoD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-v9Y5vCmxszf7JJoD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-v9Y5vCmxszf7JJoD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-v9Y5vCmxszf7JJoD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-v9Y5vCmxszf7JJoD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-v9Y5vCmxszf7JJoD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-v9Y5vCmxszf7JJoD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-v9Y5vCmxszf7JJoD .cluster text{fill:#333;}#mermaid-svg-v9Y5vCmxszf7JJoD .cluster span{color:#333;}#mermaid-svg-v9Y5vCmxszf7JJoD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-v9Y5vCmxszf7JJoD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-v9Y5vCmxszf7JJoD rect.text{fill:none;stroke-width:0;}#mermaid-svg-v9Y5vCmxszf7JJoD .icon-shape,#mermaid-svg-v9Y5vCmxszf7JJoD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-v9Y5vCmxszf7JJoD .icon-shape p,#mermaid-svg-v9Y5vCmxszf7JJoD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-v9Y5vCmxszf7JJoD .icon-shape .label rect,#mermaid-svg-v9Y5vCmxszf7JJoD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-v9Y5vCmxszf7JJoD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-v9Y5vCmxszf7JJoD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-v9Y5vCmxszf7JJoD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:排错要有固定路径
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
export interface 排错要有固定路径StageNode {
name: string;
owner: 'AbilityStage' | 'UIAbility' | 'ExtensionAbility' | 'Page' | 'Service';
responsibility: string;
}
export const 排错要有固定路径Node: 排错要有固定路径StageNode = {
name: '排错要有固定路径',
owner: 'Service',
responsibility: '把本小节的概念落到可复用工程代码中'
};
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把小节概念转成一个职责节点。写 Stage 工程时,先明确 owner,再决定代码放在哪个目录,能减少职责混乱。
7. 工程结构建议
建议把 Stage 相关代码按职责分层,不要把所有逻辑写在一个文件里。
text
entry/src/main/ets
├── entryability
├── entryabilitystage
├── extensionability
├── pages
├── components
├── services
├── models
└── utils
目录解释:
entryability放 UIAbility 入口。entryabilitystage放模块级初始化。extensionability放服务、卡片、分享等扩展能力。pages放 ArkUI 页面。services放可复用业务服务。
8. 从 Demo 到项目时怎么拆
官方示例通常为了让读者快速跑通,会把代码写得比较集中。真实项目不能一直停留在 Demo 写法,否则页面一多就会出现三个问题:入口文件越来越大、页面事件越来越重、后台和前台逻辑互相影响。
更推荐的拆法是:
AbilityStage只处理模块级初始化,例如日志、配置、轻量服务注册。UIAbility只处理界面入口,例如生命周期、窗口创建、首页加载、Want 参数接收。ExtensionAbility只处理系统扩展入口,例如服务、卡片、分享等非普通页面场景。pages只写页面状态和交互,不直接堆复杂业务。services承担可复用业务,例如任务调度、缓存、配置读取、数据请求。
一个判断标准很实用:如果这段代码离开当前页面后仍然有价值,就不要写死在页面里;如果这段代码只和启动入口有关,就不要放进组件里;如果这段代码需要系统以扩展方式调用,就不要强行放进 UIAbility。
补充流程图:
#mermaid-svg-SVAzv6IAujitwm8a{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SVAzv6IAujitwm8a .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SVAzv6IAujitwm8a .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SVAzv6IAujitwm8a .error-icon{fill:#552222;}#mermaid-svg-SVAzv6IAujitwm8a .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SVAzv6IAujitwm8a .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SVAzv6IAujitwm8a .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SVAzv6IAujitwm8a .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SVAzv6IAujitwm8a .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SVAzv6IAujitwm8a .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SVAzv6IAujitwm8a .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SVAzv6IAujitwm8a .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SVAzv6IAujitwm8a .marker.cross{stroke:#333333;}#mermaid-svg-SVAzv6IAujitwm8a svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SVAzv6IAujitwm8a p{margin:0;}#mermaid-svg-SVAzv6IAujitwm8a .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SVAzv6IAujitwm8a .cluster-label text{fill:#333;}#mermaid-svg-SVAzv6IAujitwm8a .cluster-label span{color:#333;}#mermaid-svg-SVAzv6IAujitwm8a .cluster-label span p{background-color:transparent;}#mermaid-svg-SVAzv6IAujitwm8a .label text,#mermaid-svg-SVAzv6IAujitwm8a span{fill:#333;color:#333;}#mermaid-svg-SVAzv6IAujitwm8a .node rect,#mermaid-svg-SVAzv6IAujitwm8a .node circle,#mermaid-svg-SVAzv6IAujitwm8a .node ellipse,#mermaid-svg-SVAzv6IAujitwm8a .node polygon,#mermaid-svg-SVAzv6IAujitwm8a .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SVAzv6IAujitwm8a .rough-node .label text,#mermaid-svg-SVAzv6IAujitwm8a .node .label text,#mermaid-svg-SVAzv6IAujitwm8a .image-shape .label,#mermaid-svg-SVAzv6IAujitwm8a .icon-shape .label{text-anchor:middle;}#mermaid-svg-SVAzv6IAujitwm8a .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SVAzv6IAujitwm8a .rough-node .label,#mermaid-svg-SVAzv6IAujitwm8a .node .label,#mermaid-svg-SVAzv6IAujitwm8a .image-shape .label,#mermaid-svg-SVAzv6IAujitwm8a .icon-shape .label{text-align:center;}#mermaid-svg-SVAzv6IAujitwm8a .node.clickable{cursor:pointer;}#mermaid-svg-SVAzv6IAujitwm8a .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SVAzv6IAujitwm8a .arrowheadPath{fill:#333333;}#mermaid-svg-SVAzv6IAujitwm8a .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SVAzv6IAujitwm8a .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SVAzv6IAujitwm8a .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SVAzv6IAujitwm8a .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SVAzv6IAujitwm8a .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SVAzv6IAujitwm8a .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SVAzv6IAujitwm8a .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SVAzv6IAujitwm8a .cluster text{fill:#333;}#mermaid-svg-SVAzv6IAujitwm8a .cluster span{color:#333;}#mermaid-svg-SVAzv6IAujitwm8a div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SVAzv6IAujitwm8a .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SVAzv6IAujitwm8a rect.text{fill:none;stroke-width:0;}#mermaid-svg-SVAzv6IAujitwm8a .icon-shape,#mermaid-svg-SVAzv6IAujitwm8a .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SVAzv6IAujitwm8a .icon-shape p,#mermaid-svg-SVAzv6IAujitwm8a .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SVAzv6IAujitwm8a .icon-shape .label rect,#mermaid-svg-SVAzv6IAujitwm8a .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SVAzv6IAujitwm8a .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SVAzv6IAujitwm8a .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SVAzv6IAujitwm8a :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:从 Demo 到项目时怎么拆
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
export interface 从_Demo_到项目时怎么拆StageNode {
name: string;
owner: 'AbilityStage' | 'UIAbility' | 'ExtensionAbility' | 'Page' | 'Service';
responsibility: string;
}
export const 从_Demo_到项目时怎么拆Node: 从_Demo_到项目时怎么拆StageNode = {
name: '从 Demo 到项目时怎么拆',
owner: 'Service',
responsibility: '把本小节的概念落到可复用工程代码中'
};
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把小节概念转成一个职责节点。写 Stage 工程时,先明确 owner,再决定代码放在哪个目录,能减少职责混乱。
9. 实战代码
推荐目录结构
text
entry/src/main/ets
├── entryability
│ └── EntryAbility.ets
├── entryabilitystage
│ └── EntryAbilityStage.ets
├── pages
│ ├── Index.ets
│ └── Detail.ets
├── components
│ └── FeatureCard.ets
├── services
│ ├── AppConfigService.ets
│ └── TaskService.ets
├── models
│ └── CourseItem.ets
└── utils
└── LogUtil.ets
代码解释:
- 这段代码对应本节的最小可运行思路。
- 重点不是复制粘贴,而是理解它放在 Stage 工程中的位置。
- 目录本身就是架构约束。越早稳定目录,后期越容易协作。
简单日志工具
ts
import { hilog } from '@kit.PerformanceAnalysisKit';
const DOMAIN = 0x0000;
export class LogUtil {
static info(tag: string, message: string): void {
hilog.info(DOMAIN, tag, '%{public}s', message);
}
static error(tag: string, message: string): void {
hilog.error(DOMAIN, tag, '%{public}s', message);
}
}
代码解释:
- 这段代码对应本节的最小可运行思路。
- 重点不是复制粘贴,而是理解它放在 Stage 工程中的位置。
- 统一日志入口后,调试和线上排查都会更方便。
10. 实战案例:做一个学习任务入口
下面用一个小案例把本章主题落到工程里。假设应用首页有一个"开始学习"按钮,点击后进入某个学习任务。页面只负责触发动作,任务参数和任务状态交给服务层维护。
ts
export interface StudyTask {
id: string;
title: string;
source: string;
createdAt: number;
}
export class StudyTaskService {
private static currentTask?: StudyTask;
static create(title: string, source: string): StudyTask {
const task: StudyTask = {
id: `${Date.now()}`,
title,
source,
createdAt: Date.now()
};
StudyTaskService.currentTask = task;
return task;
}
static getCurrent(): StudyTask | undefined {
return StudyTaskService.currentTask;
}
}
代码解释:
- 页面不直接拼业务对象,而是调用服务层创建任务。
StudyTask明确了任务字段,后续传参、缓存、日志都会更清楚。- 服务层可以继续扩展成持久化版本,不影响页面结构。
页面使用方式如下:
ts
@Entry
@Component
struct Index {
@State latestTitle: string = '暂无任务';
build() {
Column({ space: 16 }) {
Text('Stage 学习任务')
.fontSize(28)
.fontWeight(FontWeight.Bold)
Text(this.latestTitle)
.fontSize(16)
.fontColor('#5A6B7B')
Button('开始学习')
.height(48)
.onClick(() => {
const task = StudyTaskService.create('Stage 模型实战', 'home');
this.latestTitle = task.title;
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
这段页面代码只做三件事:显示标题、响应点击、更新 UI。真正的任务对象由服务层创建,这就是从 Demo 走向工程化的第一步。
11. 常见问题
11.1 代码写了但没有生效
先检查配置文件是否声明了对应入口,再检查路径大小写是否一致。Stage 工程里很多问题不是代码逻辑错,而是配置没有把类和系统入口连接起来。
11.2 页面和 Ability 职责混在一起
页面负责展示和交互,Ability 负责入口和生命周期。业务逻辑如果多个页面都要用,应该沉到 services 层。
11.3 后台能力被系统限制
后台任务不能按桌面端思路设计。要先判断任务是否必须立即执行、用户是否能感知、是否可以延迟,再选择对应方案。
11.4 生命周期日志看不懂
建议给每个入口统一 tag,例如 EntryAbility、AbilityStage、TaskService。调试时按 tag 过滤日志,先看入口是否执行,再看页面是否加载,最后看业务服务是否被调用。
11.5 页面能打开但状态不对
优先检查状态是否应该放在页面中。如果状态需要跨页面共享,就放进服务层或持久化存储;如果状态只影响当前页面,再使用 @State 管理。
补充流程图:
#mermaid-svg-E6O7ZmIgGdSKoTT8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .error-icon{fill:#552222;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .marker.cross{stroke:#333333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-E6O7ZmIgGdSKoTT8 p{margin:0;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .cluster-label text{fill:#333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .cluster-label span{color:#333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .cluster-label span p{background-color:transparent;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .label text,#mermaid-svg-E6O7ZmIgGdSKoTT8 span{fill:#333;color:#333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .node rect,#mermaid-svg-E6O7ZmIgGdSKoTT8 .node circle,#mermaid-svg-E6O7ZmIgGdSKoTT8 .node ellipse,#mermaid-svg-E6O7ZmIgGdSKoTT8 .node polygon,#mermaid-svg-E6O7ZmIgGdSKoTT8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .rough-node .label text,#mermaid-svg-E6O7ZmIgGdSKoTT8 .node .label text,#mermaid-svg-E6O7ZmIgGdSKoTT8 .image-shape .label,#mermaid-svg-E6O7ZmIgGdSKoTT8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .rough-node .label,#mermaid-svg-E6O7ZmIgGdSKoTT8 .node .label,#mermaid-svg-E6O7ZmIgGdSKoTT8 .image-shape .label,#mermaid-svg-E6O7ZmIgGdSKoTT8 .icon-shape .label{text-align:center;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .node.clickable{cursor:pointer;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .arrowheadPath{fill:#333333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-E6O7ZmIgGdSKoTT8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E6O7ZmIgGdSKoTT8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-E6O7ZmIgGdSKoTT8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .cluster text{fill:#333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .cluster span{color:#333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-E6O7ZmIgGdSKoTT8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .icon-shape,#mermaid-svg-E6O7ZmIgGdSKoTT8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .icon-shape p,#mermaid-svg-E6O7ZmIgGdSKoTT8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .icon-shape .label rect,#mermaid-svg-E6O7ZmIgGdSKoTT8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E6O7ZmIgGdSKoTT8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-E6O7ZmIgGdSKoTT8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-E6O7ZmIgGdSKoTT8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:常见问题
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
export interface 常见问题StageNode {
name: string;
owner: 'AbilityStage' | 'UIAbility' | 'ExtensionAbility' | 'Page' | 'Service';
responsibility: string;
}
export const 常见问题Node: 常见问题StageNode = {
name: '常见问题',
owner: 'Service',
responsibility: '把本小节的概念落到可复用工程代码中'
};
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把小节概念转成一个职责节点。写 Stage 工程时,先明确 owner,再决定代码放在哪个目录,能减少职责混乱。
12. 运行验证清单
- 修改
module.json5后重新构建安装。 - 给关键生命周期加
hilog。 - 页面路径统一使用
pages/页面名,不要写.ets后缀。 - 新增页面后同步更新
main_pages.json。 - 后台任务要记录任务状态,避免中断后无法恢复。
- 把可复用逻辑放进
services,不要散落在页面事件里。
补充流程图:
#mermaid-svg-RuubzI7YfmUcL2Iv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RuubzI7YfmUcL2Iv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RuubzI7YfmUcL2Iv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RuubzI7YfmUcL2Iv .error-icon{fill:#552222;}#mermaid-svg-RuubzI7YfmUcL2Iv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RuubzI7YfmUcL2Iv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RuubzI7YfmUcL2Iv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RuubzI7YfmUcL2Iv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RuubzI7YfmUcL2Iv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RuubzI7YfmUcL2Iv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RuubzI7YfmUcL2Iv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RuubzI7YfmUcL2Iv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RuubzI7YfmUcL2Iv .marker.cross{stroke:#333333;}#mermaid-svg-RuubzI7YfmUcL2Iv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RuubzI7YfmUcL2Iv p{margin:0;}#mermaid-svg-RuubzI7YfmUcL2Iv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RuubzI7YfmUcL2Iv .cluster-label text{fill:#333;}#mermaid-svg-RuubzI7YfmUcL2Iv .cluster-label span{color:#333;}#mermaid-svg-RuubzI7YfmUcL2Iv .cluster-label span p{background-color:transparent;}#mermaid-svg-RuubzI7YfmUcL2Iv .label text,#mermaid-svg-RuubzI7YfmUcL2Iv span{fill:#333;color:#333;}#mermaid-svg-RuubzI7YfmUcL2Iv .node rect,#mermaid-svg-RuubzI7YfmUcL2Iv .node circle,#mermaid-svg-RuubzI7YfmUcL2Iv .node ellipse,#mermaid-svg-RuubzI7YfmUcL2Iv .node polygon,#mermaid-svg-RuubzI7YfmUcL2Iv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RuubzI7YfmUcL2Iv .rough-node .label text,#mermaid-svg-RuubzI7YfmUcL2Iv .node .label text,#mermaid-svg-RuubzI7YfmUcL2Iv .image-shape .label,#mermaid-svg-RuubzI7YfmUcL2Iv .icon-shape .label{text-anchor:middle;}#mermaid-svg-RuubzI7YfmUcL2Iv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RuubzI7YfmUcL2Iv .rough-node .label,#mermaid-svg-RuubzI7YfmUcL2Iv .node .label,#mermaid-svg-RuubzI7YfmUcL2Iv .image-shape .label,#mermaid-svg-RuubzI7YfmUcL2Iv .icon-shape .label{text-align:center;}#mermaid-svg-RuubzI7YfmUcL2Iv .node.clickable{cursor:pointer;}#mermaid-svg-RuubzI7YfmUcL2Iv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RuubzI7YfmUcL2Iv .arrowheadPath{fill:#333333;}#mermaid-svg-RuubzI7YfmUcL2Iv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RuubzI7YfmUcL2Iv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RuubzI7YfmUcL2Iv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RuubzI7YfmUcL2Iv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RuubzI7YfmUcL2Iv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RuubzI7YfmUcL2Iv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RuubzI7YfmUcL2Iv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RuubzI7YfmUcL2Iv .cluster text{fill:#333;}#mermaid-svg-RuubzI7YfmUcL2Iv .cluster span{color:#333;}#mermaid-svg-RuubzI7YfmUcL2Iv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RuubzI7YfmUcL2Iv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RuubzI7YfmUcL2Iv rect.text{fill:none;stroke-width:0;}#mermaid-svg-RuubzI7YfmUcL2Iv .icon-shape,#mermaid-svg-RuubzI7YfmUcL2Iv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RuubzI7YfmUcL2Iv .icon-shape p,#mermaid-svg-RuubzI7YfmUcL2Iv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RuubzI7YfmUcL2Iv .icon-shape .label rect,#mermaid-svg-RuubzI7YfmUcL2Iv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RuubzI7YfmUcL2Iv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RuubzI7YfmUcL2Iv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RuubzI7YfmUcL2Iv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:运行验证清单
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
export interface 运行验证清单StageNode {
name: string;
owner: 'AbilityStage' | 'UIAbility' | 'ExtensionAbility' | 'Page' | 'Service';
responsibility: string;
}
export const 运行验证清单Node: 运行验证清单StageNode = {
name: '运行验证清单',
owner: 'Service',
responsibility: '把本小节的概念落到可复用工程代码中'
};
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把小节概念转成一个职责节点。写 Stage 工程时,先明确 owner,再决定代码放在哪个目录,能减少职责混乱。
13. 读者练习
建议你按下面步骤自己做一遍:
- 在现有 Stage 工程里新建一个
services目录。 - 把页面里的任务创建逻辑移动到
StudyTaskService。 - 在 Ability 生命周期里加入
hilog。 - 新增一个页面,验证页面跳转和服务层状态是否正常。
- 把可复用文字移动到
string.json。 - 把可复用颜色移动到
color.json。 - 重新运行应用,确认入口、页面、服务层职责清晰。
如果这 7 步都能跑通,说明你已经不是只会改默认模板,而是开始按工程方式组织 Stage 应用了。
补充流程图:
#mermaid-svg-Bmwb2ppolSS9opo2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bmwb2ppolSS9opo2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bmwb2ppolSS9opo2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bmwb2ppolSS9opo2 .error-icon{fill:#552222;}#mermaid-svg-Bmwb2ppolSS9opo2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bmwb2ppolSS9opo2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bmwb2ppolSS9opo2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bmwb2ppolSS9opo2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bmwb2ppolSS9opo2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bmwb2ppolSS9opo2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bmwb2ppolSS9opo2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bmwb2ppolSS9opo2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bmwb2ppolSS9opo2 .marker.cross{stroke:#333333;}#mermaid-svg-Bmwb2ppolSS9opo2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bmwb2ppolSS9opo2 p{margin:0;}#mermaid-svg-Bmwb2ppolSS9opo2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bmwb2ppolSS9opo2 .cluster-label text{fill:#333;}#mermaid-svg-Bmwb2ppolSS9opo2 .cluster-label span{color:#333;}#mermaid-svg-Bmwb2ppolSS9opo2 .cluster-label span p{background-color:transparent;}#mermaid-svg-Bmwb2ppolSS9opo2 .label text,#mermaid-svg-Bmwb2ppolSS9opo2 span{fill:#333;color:#333;}#mermaid-svg-Bmwb2ppolSS9opo2 .node rect,#mermaid-svg-Bmwb2ppolSS9opo2 .node circle,#mermaid-svg-Bmwb2ppolSS9opo2 .node ellipse,#mermaid-svg-Bmwb2ppolSS9opo2 .node polygon,#mermaid-svg-Bmwb2ppolSS9opo2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bmwb2ppolSS9opo2 .rough-node .label text,#mermaid-svg-Bmwb2ppolSS9opo2 .node .label text,#mermaid-svg-Bmwb2ppolSS9opo2 .image-shape .label,#mermaid-svg-Bmwb2ppolSS9opo2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bmwb2ppolSS9opo2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bmwb2ppolSS9opo2 .rough-node .label,#mermaid-svg-Bmwb2ppolSS9opo2 .node .label,#mermaid-svg-Bmwb2ppolSS9opo2 .image-shape .label,#mermaid-svg-Bmwb2ppolSS9opo2 .icon-shape .label{text-align:center;}#mermaid-svg-Bmwb2ppolSS9opo2 .node.clickable{cursor:pointer;}#mermaid-svg-Bmwb2ppolSS9opo2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bmwb2ppolSS9opo2 .arrowheadPath{fill:#333333;}#mermaid-svg-Bmwb2ppolSS9opo2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bmwb2ppolSS9opo2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bmwb2ppolSS9opo2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bmwb2ppolSS9opo2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bmwb2ppolSS9opo2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bmwb2ppolSS9opo2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bmwb2ppolSS9opo2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bmwb2ppolSS9opo2 .cluster text{fill:#333;}#mermaid-svg-Bmwb2ppolSS9opo2 .cluster span{color:#333;}#mermaid-svg-Bmwb2ppolSS9opo2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bmwb2ppolSS9opo2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bmwb2ppolSS9opo2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bmwb2ppolSS9opo2 .icon-shape,#mermaid-svg-Bmwb2ppolSS9opo2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bmwb2ppolSS9opo2 .icon-shape p,#mermaid-svg-Bmwb2ppolSS9opo2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bmwb2ppolSS9opo2 .icon-shape .label rect,#mermaid-svg-Bmwb2ppolSS9opo2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bmwb2ppolSS9opo2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bmwb2ppolSS9opo2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bmwb2ppolSS9opo2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:读者练习
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
export interface 读者练习StageNode {
name: string;
owner: 'AbilityStage' | 'UIAbility' | 'ExtensionAbility' | 'Page' | 'Service';
responsibility: string;
}
export const 读者练习Node: 读者练习StageNode = {
name: '读者练习',
owner: 'Service',
responsibility: '把本小节的概念落到可复用工程代码中'
};
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把小节概念转成一个职责节点。写 Stage 工程时,先明确 owner,再决定代码放在哪个目录,能减少职责混乱。
14. 本章总结
学完 Stage 模型后,真正落地项目要解决目录、职责、服务、排错和发布前检查。本文给出一套可执行清单。 真正写项目时,不要先问"这个 API 怎么调",而要先问"这个能力应该属于哪一层"。职责边界清楚,Stage 工程才会越写越稳。
参考资料
- 华为开发者文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/stage-model-development-overview
- 华为开发者文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-background-tasks-1
- 华为开发者文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/start-with-ets-stage
补充流程图:
#mermaid-svg-HJI5U8QutVCkV9sV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HJI5U8QutVCkV9sV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HJI5U8QutVCkV9sV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HJI5U8QutVCkV9sV .error-icon{fill:#552222;}#mermaid-svg-HJI5U8QutVCkV9sV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HJI5U8QutVCkV9sV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HJI5U8QutVCkV9sV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HJI5U8QutVCkV9sV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HJI5U8QutVCkV9sV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HJI5U8QutVCkV9sV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HJI5U8QutVCkV9sV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HJI5U8QutVCkV9sV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HJI5U8QutVCkV9sV .marker.cross{stroke:#333333;}#mermaid-svg-HJI5U8QutVCkV9sV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HJI5U8QutVCkV9sV p{margin:0;}#mermaid-svg-HJI5U8QutVCkV9sV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HJI5U8QutVCkV9sV .cluster-label text{fill:#333;}#mermaid-svg-HJI5U8QutVCkV9sV .cluster-label span{color:#333;}#mermaid-svg-HJI5U8QutVCkV9sV .cluster-label span p{background-color:transparent;}#mermaid-svg-HJI5U8QutVCkV9sV .label text,#mermaid-svg-HJI5U8QutVCkV9sV span{fill:#333;color:#333;}#mermaid-svg-HJI5U8QutVCkV9sV .node rect,#mermaid-svg-HJI5U8QutVCkV9sV .node circle,#mermaid-svg-HJI5U8QutVCkV9sV .node ellipse,#mermaid-svg-HJI5U8QutVCkV9sV .node polygon,#mermaid-svg-HJI5U8QutVCkV9sV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HJI5U8QutVCkV9sV .rough-node .label text,#mermaid-svg-HJI5U8QutVCkV9sV .node .label text,#mermaid-svg-HJI5U8QutVCkV9sV .image-shape .label,#mermaid-svg-HJI5U8QutVCkV9sV .icon-shape .label{text-anchor:middle;}#mermaid-svg-HJI5U8QutVCkV9sV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HJI5U8QutVCkV9sV .rough-node .label,#mermaid-svg-HJI5U8QutVCkV9sV .node .label,#mermaid-svg-HJI5U8QutVCkV9sV .image-shape .label,#mermaid-svg-HJI5U8QutVCkV9sV .icon-shape .label{text-align:center;}#mermaid-svg-HJI5U8QutVCkV9sV .node.clickable{cursor:pointer;}#mermaid-svg-HJI5U8QutVCkV9sV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HJI5U8QutVCkV9sV .arrowheadPath{fill:#333333;}#mermaid-svg-HJI5U8QutVCkV9sV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HJI5U8QutVCkV9sV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HJI5U8QutVCkV9sV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HJI5U8QutVCkV9sV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HJI5U8QutVCkV9sV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HJI5U8QutVCkV9sV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HJI5U8QutVCkV9sV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HJI5U8QutVCkV9sV .cluster text{fill:#333;}#mermaid-svg-HJI5U8QutVCkV9sV .cluster span{color:#333;}#mermaid-svg-HJI5U8QutVCkV9sV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HJI5U8QutVCkV9sV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HJI5U8QutVCkV9sV rect.text{fill:none;stroke-width:0;}#mermaid-svg-HJI5U8QutVCkV9sV .icon-shape,#mermaid-svg-HJI5U8QutVCkV9sV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HJI5U8QutVCkV9sV .icon-shape p,#mermaid-svg-HJI5U8QutVCkV9sV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HJI5U8QutVCkV9sV .icon-shape .label rect,#mermaid-svg-HJI5U8QutVCkV9sV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HJI5U8QutVCkV9sV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HJI5U8QutVCkV9sV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HJI5U8QutVCkV9sV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 理解:本章总结
确定代码位置
编写最小示例
运行验证
沉淀到工程结构
补充代码:
ts
export interface 本章总结StageNode {
name: string;
owner: 'AbilityStage' | 'UIAbility' | 'ExtensionAbility' | 'Page' | 'Service';
responsibility: string;
}
export const 本章总结Node: 本章总结StageNode = {
name: '本章总结',
owner: 'Service',
responsibility: '把本小节的概念落到可复用工程代码中'
};
代码解释:
- 流程图先把本小节从概念、代码位置、实现、验证串起来,读者不会只看到一段抽象描述。
- 示例代码给出一个可以迁移到 Stage 工程中的最小写法。
- 这段代码把小节概念转成一个职责节点。写 Stage 工程时,先明确 owner,再决定代码放在哪个目录,能减少职责混乱。