《Web 到 HarmonyOS》-- Ability、Stage、模块化与路由导航

前端同学最容易卡在这一层:

Vue 里我有 main.tsApp.vuevue-router;鸿蒙里突然冒出 Ability、Stage、Module、routerNavigation------它们分别对应什么? 一句话对齐:

Web(Vue) HarmonyOS(Stage 模型)
整个 SPA 应用 一个或多个 Ability 组成的应用
浏览器窗口 / Tab WindowStage(窗口舞台)
router 管理页面栈 页面路由 router + 组件导航 Navigation
monorepo / 分包 / 组件库 Module:entry / feature / HAR / HSP

本篇按「从外到内」讲清四件事:Stage → Ability → 模块化 → 路由,并用个人的学习仓库代码对照。


一、先建立总图:应用是怎么被系统拉起来的

flowchart TD A[用户点击图标或Want拉起] --> B[创建UIAbility] B --> C[onCreate] C --> D[onWindowStageCreate] D --> E[loadContent加载首页] E --> F[页面Entry组件渲染] F --> G{用户操作} G -->|切后台| H[onBackground] G -->|回前台| I[onForeground] G -->|页面内跳转| J[router或Navigation] H --> I B --> K[onDestroy]

对照本仓库入口:系统先找到 module.json5 里的 mainElement: EntryAbility,再执行 EntryAbility,在窗口创建时加载 pages/Index

21:36:entry/src/main/ets/entryability/EntryAbility.ets 复制代码
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    font.registerFont({
      familyName: 'iconfont',
      familySrc: $rawfile('iconfont.ttf'),
    });

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    });
  }

对前端的映射:

  • EntryAbilitymain.tscreateApp(App).mount('#app') 之前的「应用壳」
  • loadContent('pages/Index') ≈ 指定第一个路由页面
  • 字体注册、主题色模式等「全局初始化」适合放在 Ability,而不是某个业务页

二、Stage 模型:为什么不是「只有页面生命周期」

鸿蒙当前主流是 Stage 模型 (相对早期的 FA 模型)。它把应用运行抽象成多个阶段(Stage) ,由系统统一调度资源与回调,而不是只让你在页面里写 onMounted

结合 Stage 模型解读 的核心概念:

概念 含义 前端类比
Stage(阶段) 应用在系统中的运行状态单位(前台 / 后台 / 暂停 / 销毁等),对应不同资源策略 浏览器标签前台 / 后台 / 被回收
Ability(能力组件) 功能载体:UIAbility、ExtensionAbility 等,挂在 Stage 上跑 一个「可独立拉起」的子应用壳
WindowStage UIAbility 持有的窗口舞台,负责加载页面内容 浏览器窗口 / Electron BrowserWindow

Stage 带来的实际好处

  1. 资源策略更细:前台可全量渲染;后台应暂停动画、少刷 UI;内存压力时可释放缓存。
  2. 多端 / 多窗口更统一:同一套 Ability + Stage 回调,便于理解跨设备、分屏场景。
  3. 生命周期可控:初始化、前后台切换、销毁都有明确钩子,避免「页面还在跑轮询、应用已经切后台」。

对前端的提醒:Vue 的 onMounted / onUnmounted 管不了 「整个 App 进后台」;鸿蒙要把这类逻辑放到 Ability 的 onForeground / onBackground


三、Ability:应用能力的基本单元

3.1 常见 Ability 类型

类型 作用 Vue / Web 类比
UIAbility 带界面的主能力,最常用 主 SPA 壳
ExtensionAbility 扩展能力(卡片、备份、分享等) 插件 / Widget / 系统扩展点
Service 类后台能力(按版本与 Kit 演进) 偏后台任务、数据同步 Web Worker / 后台 Service(注意权限与功耗)

本仓库 module.json5 里同时声明了主 Ability 和备份 Extension:

13:48:entry/src/main/module.json5 复制代码
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        ...
        "exported": true,
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["ohos.want.action.home"]
          }
        ]
      }
    ],
    "extensionAbilities": [
      {
        "name": "EntryBackupAbility",
        "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
        "type": "backup",
        ...
      }
    ]

skills 里的 ohos.want.action.home 可以理解为:系统桌面点击图标时用什么 Want 拉起你------类似 Web 的「默认入口 URL」。

3.2 UIAbility 生命周期(必背)

结合官方实践与 Stage 生命周期说明,日常开发最常用的是:

回调 何时触发 适合做什么 Vue 对照
onCreate Ability 创建 全局配置、读启动参数 Want main.ts 初始化
onWindowStageCreate 窗口舞台创建 loadContent、注册字体 挂载根组件
onForeground 回到前台 恢复动画、刷新关键数据 visibilitychange / 回前台
onBackground 进入后台 暂停定时器、保存草稿、停动画 切后台
onWindowStageDestroy 窗口销毁 释放 UI 相关资源 卸载根
onDestroy Ability 销毁 取消订阅、清全局资源 应用退出
onMemoryLevel 内存压力变化 丢缓存、缩图片内存 少见,但原生很关键

本仓库已有的钩子骨架:

7:52:entry/src/main/ets/entryability/EntryAbility.ets 复制代码
export default class EntryAbility extends UIAbility {
  onCreate(...) { ... }
  onDestroy() { ... }
  onWindowStageCreate(windowStage) { ... loadContent ... }
  onWindowStageDestroy() { ... }
  onForeground() { ... }
  onBackground() { ... }
}

3.3 页面级生命周期(别和 Ability 混)

页面组件还有自己的约生命周期(如 aboutToAppear / aboutToDisappear 等),对应 Vue 的 onMounted / onUnmounted

分层原则:

  • App 级(登录态校验、前后台、全局字体)→ Ability
  • 页面级(请求列表、解绑监听)→ 页面组件生命周期
  • 组件级(输入框焦点、局部动画)→ 组件自身

四、模块化:工程怎么拆,对应 Web 什么

鸿蒙应用按 Module 组织,产物最终打进 HAP / HSP 等包。常见类型:

模块类型 角色 前端类比
entry 主入口模块,含主 Ability 主应用 apps/web
feature 可独立安装的功能模块 业务分包 / 微前端子应用
HAR 静态共享包(源码/资源复用) 内部 npm 组件库
HSP 动态共享包(运行时可共享) 运行时共享依赖 / DLL 思路

本仓库当前是典型的单 entry:

1:6:entry/src/main/module.json5 复制代码
{
  "module": {
    "name": "entry",
    "type": "entry",
    "mainElement": "EntryAbility",

页面清单在 main_pages.json 注册------没注册的页面,路由跳不过去 (类似没配进 vue-router 的路由表):

1:7:entry/src/main/resources/base/profile/main_pages.json 复制代码
{
  "src": [
    "pages/Index",
    "pages/login",
    "pages/Main/home"
  ]
}

模块化落地建议(给前端同学):

  1. 先单 entry 跑通业务,再拆 feature / HAR。
  2. 公共 UI、工具、model → HAR;大功能域(商城、消息)再考虑 feature。
  3. 配置(module.json5pages、权限)和代码目录一起改,否则「文件有了、系统不认」。

五、路由与导航:两套体系,别混用场景

鸿蒙日常会遇到两种「跳转」:

5.1 页面路由 router(页面级,偏 vue-router)

适用于:登录 → 首页、设置页、协议页等整页切换

常见 API 心智:

API 思路 含义 Vue Router
pushUrl 入栈打开新页面 router.push
replaceUrl 替换当前页(无返回) router.replace
back 返回 router.back
传参 params / 启动参数 query / params

本仓库登录成功注释里已预留:

ts 复制代码
// 登录成功后跳转:router.replaceUrl({ url: 'pages/Main/home' });

注意:

  1. url 必须能在 main_pages.json 里找到对应 path。
  2. 登录成功常用 replaceUrl ,避免返回键又回到登录页(和 Vue 里 replace: true 一样)。
  3. 需要带复杂对象时,优先用明确字段 / 状态层,而不是塞巨型 JSON。

适用于:首页 Tab 内再推详情、个人中心里多层子页------一个 Ability 内的导航栈

本仓库主页结构就是典型「多 Tab × 多栈」:

8:56:entry/src/main/ets/pages/Main/main.ets 复制代码
  @State currentTab: number = 0;
  private homeStack: NavPathStack = new NavPathStack();
  private profileStack: NavPathStack = new NavPathStack();
  ...
  Tabs({ barPosition: BarPosition.End, index: this.currentTab }) {
    TabContent() {
      Navigation(this.homeStack) {
        HomePage()
      }
      .mode(NavigationMode.Stack)
    }
    ...
    TabContent() {
      Navigation(this.profileStack) {
        ProfilePage()
      }
      .mode(NavigationMode.Stack)
    }
  }

对前端的映射:

场景 Vue 常见写法 鸿蒙推荐
登录进首页 replace('/home') router.replaceUrl
Tab 切换 嵌套路由 / keep-alive Tabs + 各自 NavPathStack
Tab 内进详情 children 路由 homeStack.pushPath(...)
跨 Ability 拉起 几乎没有 Want 启动另一个 Ability

更多路由细节与踩坑,可对照社区实践:路由导航相关博文

5.3 选型口诀

换「整页入口」用 router;在「当前业务流里往下钻」用 Navigation

Tab 场景优先「一 Tab 一栈」,返回体验才像原生 App。


六、和 Vue 生命周期 / 路由的一张总对照表

你在 Vue 里做的事 鸿蒙落点
main.ts 创建应用 UIAbility.onCreate + onWindowStageCreate
App.vue 根布局 loadContent 加载的首个 @Entry 页面
router.push/replace router.pushUrl/replaceUrl
嵌套路由 / 多级详情 Navigation + NavPathStack
onMounted / onUnmounted 页面 aboutToAppear / aboutToDisappear
监听页面可见性 / 前后台 Ability onForeground / onBackground
微前端 / monorepo 分包 feature 模块 + HAR/HSP
环境变量 / 全局插件 Ability 初始化 + AppStorage 等

七、实战建议(按本仓库可立刻做的)

  1. 理清启动链 :桌面图标 → EntryAbilityloadContent('pages/Index') → 再决定是否进 login / Main
  2. 登录跳转用 replaceUrl ,并保证目标页已写入 main_pages.json
  3. 主框架用 Tabs + 双 NavPathStack (你已具备),详情页往对应 stack 推,而不是到处 router.push
  4. 在 Ability 的 onBackground 停掉轮询 / 动画,避免后台耗电(Stage 模型的核心价值之一)。
  5. 模块先不急着拆:业务稳定后再抽 HAR;过早拆模块会同时踩配置与依赖两个坑。

八、本篇结论

  • Stage 解决的是「应用在系统里处于哪一阶段、资源怎么管」。
  • Ability 解决的是「谁被系统拉起、窗口与全局生命周期放哪」。
  • 模块化 解决的是「工程如何拆包复用」,对应 Web 的应用 / 分包 / 组件库。
  • 路由 分两层:router 管页面,Navigation 管 Ability 内导航栈。

前端转鸿蒙,先把这四层叠起来,后面写业务页才不会「路由乱跳、后台还在请求、页面注册了却进不去」。

相关推荐
ShineWinsu1 小时前
对于 Vue 3:从为什么学 Vue,到声明式渲染与数据响应式的解析
前端·javascript·vue.js
粥里有勺糖1 小时前
视野修炼-技术周刊第132期 | 一些有趣的组件
前端·github·aigc
এ慕ོ冬℘゜2 小时前
样式控制 .css ()
前端·css
leoZ2313 小时前
第 6 篇:SchemaForm 渲染器核心实现
前端·javascript·vue.js·人工智能·神经网络·机器学习·自然语言处理
CoderYanger3 小时前
前端基础——JavaScript(基础语法)(下篇)
java·开发语言·前端·javascript·程序人生·面试·职场和发展
还是大剑师兰特3 小时前
vue项目浏览器版本判别,低于IE11跳转到新页面
前端·javascript·vue.js
计算机魔术师3 小时前
奥尔特曼致歉 GPT-6 Astra 发布混乱,现已面向所有 Plus / Pro 等用户推出
前端
落樱弥城3 小时前
RHI 设计考量:不同 API 差异分析
服务器·前端·性能优化
热心网友俣先生4 小时前
2026国赛C题:五年命题规律与预测
java·c语言·前端·数学建模