HarmonyKit | 鸿蒙新特性:router 导航 API 从 pushUrl 到 UIContext 的演进
引言:一个全局 API 的谢幕
HarmonyKit 初期使用全局router.pushUrl()做页面跳转。后来 DevEco Studio 的编译器开始对router.pushUrl()报 deprecated 警告:“推荐使用this.getUIContext().getRouter().pushUrl()”。从全局函数到 UI 上下文绑定——这个 API 演进背后是鸿蒙多窗口能力的底层架构变化。
这篇文章梳理了 router API 的演进时间线、迁移前后的代码对比、同样受影响的promptAction.showToast的迁移路径、以及 NavPathStack 作为下一代导航方案的展望。
项目仓库:https://atomgit.com/VON-/harmony-kit
为什么废弃全局 router
全局 router 的核心问题是:在支持多窗口的 HarmonyOS 设备上,全局 router 不知道"当前应该操作哪个窗口的页面栈"。
场景:应用在分屏模式下有两个窗口,分别是"主页"和"工具页"。用户在主窗口的 ToolCard 上点了一下,跳转到 JSON 格式化工具。全局router.pushUrl()应该把页面推入哪个窗口的栈?全局 router 无法判断——它只知道"有一个 pushUrl 调用",但不知道调用来自哪个 UI 上下文。
UIContext 绑定 router 解决了这个问题。this.getUIContext()返回当前组件所在的 UI 上下文。getRouter()返回的是这个上下文专属的路由实例。主窗口的点击推入主窗口的栈,副窗口的点击推入副窗口的栈——各走各路。
这个演进不仅是鸿蒙系统的独有趋势。Android 的startActivity在分屏模式下同样需要指定ActivityOptions来控制在哪个屏幕启动 Activity。iOS 的UISceneAPI 也要求场景感知的导航。多窗口是操作系统发展的必然方向,而从全局 API 到上下文绑定 API 的迁移,是所有框架都必须经历的架构调整。
迁移代码对比
// === 旧写法(已废弃)===import{router}from'@kit.ArkUI';// 跳转router.pushUrl({url:'pages/tools/JsonFormatter'});// 返回router.back();// === 新写法(推荐)===// 不需要 import router// 跳转this.getUIContext().getRouter().pushUrl({url:'pages/tools/JsonFormatter'});// 返回this.getUIContext().getRouter().back();改动的核心:不再依赖全局 router 对象,而是通过当前 UI 上下文获取路由实例。多窗口场景下每个窗口的路由栈独立,但 HarmonyKit 作为手机单窗口应用,新旧写法在功能上没有差异——迁移纯粹是为了消除 compiler warning 和拥抱未来兼容性。
pushUrl 的完整参数
新版本pushUrl支持完整的 Options 配置:
this.getUIContext().getRouter().pushUrl({url:'pages/tools/JsonFormatter',params:{input:'{"test": true}'}// 携带参数到目标页面});目标页面通过@State或@Prop接收参数。在之前的 HarmonyKit 版本中,工具页不需要接收参数(所有工具都是从空白状态启动),但这个能力在更复杂的导航场景中至关重要。
promptAction.showToast 的同样命运
// === 旧写法 ===import{promptAction}from'@kit.ArkUI';promptAction.showToast({message:'已复制到剪贴板'});// === 新写法 ===this.getUIContext().getPromptAction().showToast({message:'已复制到剪贴板'});废弃原因类似——全局promptAction在异步场景(setTimeout、Promise 回调)中会丢失 UI 上下文,导致 Toast 无法弹出。UIContext 绑定保证了异步回调中也能正确弹 Toast。
这是一个典型的问题:在 HarmonyKit 的哈希计算工具中,用户输入 → 点击计算 → 结果展示 → 点击复制(CopyButton)→ showToast。这个过程涉及多个异步步骤(哈希计算是 async/await),如果 showToast 是全局调用,在异步链路中可能丢失 UI 上下文。
HarmonyKit 的迁移方案
CopyButton 组件是 HarmonyKit 中唯一调用showToast的地方——共 10 个工具页的复制按钮共享此组件。迁移时只改 CopyButton 一个文件,10 个工具自动生效。这是组件化架构的优势——全局 API 迁移的成本被限制在一个组件内部。
此外,每个工具页都有独立的返回按钮(顶部导航栏),调用router.back()。迁移到 UIContext 后,每个页面的返回按钮代码从:
router.back();改为:
this.getUIContext().getRouter().back();同样是"每页面改一行"的工作量。10 个工具页共 10 行修改,没有遗漏风险。
鸿蒙 API 废弃策略
鸿蒙的 API 废弃遵循"标记 → 警告 → 移除"的渐进策略。@deprecated标记的 API 仍然可用,但编译器输出 warning。开发者有充分的时间窗口进行迁移。HarmonyKit 在收到 warning 后统一迁移——改动量不大(每个受影响的方法只改一行)。
从实际体验来看,这个策略的设计是合理的:
- 标记阶段:API 新增替代方法,旧方法标记为
@deprecated,文档同步更新。开发者可以选择立即迁移或等更成熟。 - 警告阶段:编译器开始输出 warning,但功能不受影响。开发者收到 warning 后安排迁移时间。
- 移除阶段:API 真正被移除,调用导致编译错误。通常跨越 3-5 个 API 版本,给开发者充分的迁移时间。
HarmonyKit 遇到的废弃 API 都处于警告阶段——编译不报错,只是输出 warning。但"warning 也是技术债",尽早迁移是合理的工程实践。
NavPathStack:下一代导航
NavPathStack配合HdsNavigation的navDestinationbuilder 是官方推荐的长期导航方案。它提供了声明式的路由映射、类型安全的路由参数、以及页面的自动释放和重建。
// NavPathStack 的基本用法@StatepathStack:NavPathStack=newNavPathStack();build(){HdsNavigation(this.pathStack){// 声明路由表HdsNavDestination(){if(name==='JsonFormatter'){JsonFormatterPage();}// 更多路由...}.title(...)}}// 跳转this.pathStack.pushPath({name:'JsonFormatter'});NavPathStack 相比 router 的核心优势:
- 声明式路由映射:路由表集中管理,一目了然
- 类型安全:路由参数的类型在编译期检查
- 自动释放:页面出栈后自动销毁,减少内存占用
- 生命周期集成:与 HdsNavigation 的 titleBar、toolbar 无缝集成
HarmonyKit 目前的页面栈深度不超过 2 层(主页 → 工具页),router 够用。对于更复杂的应用(多级导航、嵌套 Tab、动态路由注册),NavPathStack 是值得考虑的迁移目标。
router 和 NavPathStack 的对比
| 维度 | router | NavPathStack |
|---|---|---|
| API 形式 | 全局函数 | 实例方法 |
| 多窗口支持 | 不支持(废弃原因) | 原生支持 |
| 参数类型安全 | 运行时检查 | 编译期检查 |
| 路由声明 | 独立 JSON 文件 | 集中式 builder |
| 页面释放 | 手动管理 | 自动管理 |
| 与 HDS 集成 | 需要手动适配 | 原生集成 |
| 学习成本 | 低 | 中 |
对于新项目,建议直接使用 NavPathStack。对于已有项目如 HarmonyKit,迁移需要权衡:当前的 router 方案在所有设备上都能正常运行,迁移到 NavPathStack 需要重写主页的导航逻辑和所有工具页的路由声明。在没有实际痛点的情况下,这是一个"可以做但不是必须做"的改进项。
项目仓库:https://atomgit.com/VON-/harmony-kit