【句匠|14】HarmonyOS ArkTS 主导航实战:统一页面入口、返回路径和参数校验

一个 HarmonyOS 应用页面不多时,导航代码通常从几行 router.pushUrl() 开始。等首页有五个主 Tab、搜索、分类、题库详情、练习、考试结果、设置等页面后,问题会迅速变复杂:有的入口应该切 Tab,有的入口应该压入路由栈;同一个题库可能进入专属详情页,也可能进入通用详情页;设置页返回前还要改变主页 Tab;分类搜索带可选参数,练习页又依赖 bankId 和 mode。如果这些语义没有分层,返回路径、参数缺失和重复页面都会一起出现。
本文基于句匠项目的真实 ArkTS 源码展开,主要核对 entry/src/main/ets/pages/Index.ets、entry/src/main/ets/views/HomePage.ets,并用 SearchPage.ets、PracticePage.ets、SettingsPage.ets、BankCard.ets 与 TopBar.ets 验证路由发送、接收和返回行为。文章先还原源码已有的导航模型,再给出不改变现有页面结构的收口方法。
本文唯一源码标识:com.jiaweikang.one18。
源码可证明的是:五个主页面使用 AppStorage 的 currentTabIndex 切换;二级页面使用 router.pushUrl();Splash 到主页和答题到结果等替换场景使用 replaceUrl();通用顶部栏使用 router.back();搜索、题库详情、练习与考试结果页面通过 router.getParams() 接收参数并做不同程度的兜底。项目当前没有统一 NavigationService、路由常量表、运行时参数 Schema、深链或跨 Ability 导航,本文不会把建议写成已经实现的能力。
一、先区分两类导航:切 Tab 与进路由栈
句匠的主页 Index 里有首页、题库、错题本、收藏夹、我的五个主入口。它们不是五个独立路由,而是同一个 Index 页面内部的五个内容组件:
@Builder
PageContent() {
if (this.currentIndex === 0) {
HomePage()
} else if (this.currentIndex === 1) {
BankListPage()
} else if (this.currentIndex === 2) {
ExamTab()
} else if (this.currentIndex === 3) {
FavoritePage()
} else {
MinePage()
}
}
当前索引来自:
@StorageLink('currentTabIndex') currentIndex: number = 0
点击底部导航项只修改索引:
.onClick(() => {
this.currentIndex = index
})
这类操作没有新增路由记录。用户从首页切到题库,再切到收藏夹,仍停留在 Index 这个页面上。系统返回不会按“收藏夹→题库→首页”的顺序逐个回退,因为这些切换不是路由栈历史。
另一类是从主页进入搜索、分类、详情、设置等二级页。首页使用:
router.pushUrl({ url: 'pages/SearchPage' })
pushUrl 会把目标页放到当前路由栈顶部,二级页使用 router.back() 返回到之前的 Index。这两类导航必须分开,否则主 Tab 每切一次都压栈,用户按返回键会在一级页面间反复回退;反过来,如果所有二级页也只改全局状态,就失去了清晰的页面层级和返回路径。

| 用户意图 | 句匠源码做法 | 是否新增路由记录 | 返回行为 |
|---|---|---|---|
| 首页切到题库 | currentTabIndex = 1 | 否 | 仍由 Index 承担 |
| 首页切到错题本 | currentTabIndex = 2 | 否 | 仍由 Index 承担 |
| 打开搜索页 | router.pushUrl() | 是 | router.back() 返回首页 |
| 打开设置页 | router.pushUrl() | 是 | 返回前可修改主 Tab |
| Splash 进入主页 | router.replaceUrl() | 替换当前记录 | 不返回 Splash |
| 答题进入考试结果 | router.replaceUrl() | 替换答题页 | 避免回到已提交状态 |
导航稳定的第一条规则,就是先判断用户是在同层切换,还是进入新的页面层级。
二、Index 是主导航唯一状态源
Index.ets 同时绘制内容区和导航项,图标、文字、页面内容都由同一个 currentIndex 派生:
@Builder
BottomNavItem(index: number) {
Stack() {
Column({ space: 3 }) {
Image(
this.currentIndex === index
? this.tabs[index].iconSelected
: this.tabs[index].iconNormal
)
Text(this.tabs[index].title)
.fontColor(
this.currentIndex === index
? Colors.PRIMARY
: Colors.TEXT_HINT
)
.fontWeight(
this.currentIndex === index
? FontWeight.Bold
: FontWeight.Normal
)
}
}
.onClick(() => {
this.currentIndex = index
})
}
内容区的 PageContent()、底部导航图标和文字样式都读取同一个索引,没有单独维护“选中的图标”“当前页面名称”等冗余状态。这样点击一个 Tab 只发生一次状态写入,ArkUI 重建时三处表现自然一致。
HomePage 也通过同一个键取得链接:
@StorageLink('currentTabIndex') currentTabIndex: number = 0
首页“开始纠错”卡片直接把索引改为题库:
.onClick(() => {
this.currentTabIndex = 1
})
“限时挑战”切到索引 2,“错题本”则先指定收藏页内部子索引,再切到索引 3:
this.ActionCard('错题本', `${this.wrongRecords.length} 道错题`, () => {
this.favoriteTabIndex = 2
this.currentTabIndex = 3
})
这里体现了主导航的层次:currentTabIndex 决定 Index 的一级内容,favoriteTabIndex 决定收藏夹内部要显示的子分区。两个状态都来自 AppStorage,但职责不同,不能合并成一个难以解释的数字。
当前源码中的一级索引含义是稳定的:
| 索引 | 页面组件 | 首页入口示例 |
|---|---|---|
| 0 | HomePage | 默认主页 |
| 1 | BankListPage | 推荐题库“更多”、开始纠错 |
| 2 | ExamTab | 限时挑战、考试记录 |
| 3 | FavoritePage | 错题本、收藏相关入口 |
| 4 | MinePage | 我的 |
如果后续增删 Tab,应同时更新 tabs、PageContent() 和所有跨组件索引写入点。更稳的维护方式是用枚举或常量替代散落的 1/2/3,但文章不能声称当前源码已经完成这项封装。
三、HomePage 把入口按意图分成三种
首页上的入口看起来都是可点击卡片,实际承担三种不同导航语义。
第一种是一级 Tab 切换,例如“推荐题库”的“更多”:
SectionHeader({
title: '推荐题库',
actionText: '更多 >',
onAction: () => {
this.currentTabIndex = 1
}
})
第二种是无参数二级页,例如搜索入口、分类总览、每日打卡、段位、排行榜和 AI 纠错:
router.pushUrl({ url: 'pages/SearchPage' })
router.pushUrl({ url: 'pages/CategoryPage' })
router.pushUrl({ url: 'pages/DailyCheckInPage' })
router.pushUrl({ url: 'pages/LevelProgressPage' })
router.pushUrl({ url: 'pages/LeaderboardPage' })
router.pushUrl({ url: 'pages/AICorrectPage' })
第三种是带参数二级页。点击题型分类时,首页把类型和值一起交给搜索页:
router.pushUrl({
url: 'pages/SearchPage',
params: {
categoryType: cat.type,
categoryName: cat.name
}
})
三种入口不能只看组件外观。相同的 FeatureCard 可以打开一个路由页,相同的 ActionCard 也可以只切换 Tab。句匠通过回调把动作传给复用组件:
@Builder
FeatureCard(
icon: Resource,
title: string,
sub: string,
onTap: () => void
) {
Row({ space: 10 }) {
Image(icon)
Column() {
Text(title)
Text(sub)
}
}
.onClick(() => {
onTap()
})
}
复用组件只负责触发动作,不理解路由路径或主 Tab 索引。导航意图仍由调用处决定,这比让卡片组件根据标题字符串猜测目标页更可靠。
四、主 Tab 切换为什么不该使用 pushUrl
假设把五个主页面都注册为路由,然后底部按钮统一调用 router.pushUrl(),表面上代码很整齐,实际会产生三类问题。
首先是返回栈膨胀。用户连续点击“首页→题库→收藏夹→我的”,路由栈会留下多个一级页面。按系统返回时,不是退出当前主界面,而是逐页倒退。
其次是共享状态容易分裂。当前 Index 统一持有 wrongRecords、断点值和安全区值,并根据索引切换内容。如果主页面各自成为独立路由,需要重新决定这些页面的共同容器、导航栏和状态所有权。
最后是重复点击。用户已经位于题库页时再次点击题库 Tab,索引赋相同值不会新增历史;若使用 pushUrl,则可能重复压入同一个页面。
句匠当前做法属于“单主页容器 + 状态切换”:
Column() {
Stack() {
this.PageContent()
}
.layoutWeight(1)
Row() {
this.BottomNavItem(0)
this.BottomNavItem(1)
this.BottomNavItem(2)
this.BottomNavItem(3)
this.BottomNavItem(4)
}
}
这一结构也集中处理了底部导航栏和系统安全区。一级页面不用各自重复绘制底栏,选中态也不会分散到五个路由页面里。
需要说明的真实边界是:PageContent() 使用条件分支创建当前组件。文章不据此声称五个页面状态都会永久保留;组件切换后的内部短期状态是否保留,应按 ArkUI 实际生命周期与组件状态设计验证,不能只从 Tab 外观推断。
五、二级页面用 pushUrl 建立可预测返回路径
搜索、分类、统计、设置等页面属于从主页进一步进入的任务页。它们使用 pushUrl,并在页面顶部提供返回入口。
公共 TopBar 的返回按钮实现很直接:
@Component
export struct TopBar {
title: string = ''
showBack: boolean = true
rightIcon?: Resource = undefined
onRightClick?: () => void = undefined
build() {
Row() {
if (this.showBack) {
Stack() {
Image($r('app.media.ic_common_back'))
}
.width(46)
.height(46)
.onClick(() => {
router.back()
})
} else {
Blank().width(46)
}
Text(this.title)
.layoutWeight(1)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
}
}
TopBar 不硬编码“返回到 Index”,而是执行 router.back()。这样从哪里进入,就回到哪里。例如设置页从“我的”进入,返回后仍回到原来的 Index 页面;学习统计若从设置页进入,返回先回设置页,而不是跨层跳回主页。
搜索页没有复用 TopBar,但自定义头部同样调用:
.onClick(() => {
router.back()
})
从导航语义看,两者一致。工程上若要继续统一视觉和安全区处理,可以让更多二级页复用 TopBar;但某些页面有搜索框等特殊头部,保留自定义组件也合理。统一的是返回行为和层级,不必强迫所有页面拥有完全相同的布局。
六、返回前切换主 Tab:先改状态,再 back
设置页有一个很有代表性的场景:用户从“我的”进入设置,然后点击某个数据入口,希望回到主页的收藏夹或考试 Tab。源码没有在设置页里重新打开一个 Index,而是先修改共享状态,再回退当前路由:
private openFavoriteTab(tabIndex: number): void {
this.pendingClear = false
this.favoriteTabIndex = tabIndex
this.currentTabIndex = 3
router.back()
}
private openMainTab(tabIndex: number): void {
this.pendingClear = false
this.currentTabIndex = tabIndex
router.back()
}
执行顺序很重要:
- 清理设置页的临时确认状态;
- 写入目标子 Tab 或主 Tab;
- 调用
router.back()弹出设置页; - 原来的 Index 重新可见,并读取更新后的
AppStorage。
如果反过来先 back() 再修改状态,页面生命周期切换可能让后续写入的时机变得难以观察。当前源码用同步赋值后回退,语义清楚。
这种模式适合“二级页完成操作后回到既有主页,并指定一个主区域”。它与 router.replaceUrl({ url: 'pages/Index' }) 不同:后者会重新创建或替换路由记录,可能丢失原有入口层级;前者复用现有 Index,只改变其状态。
前提是设置页确实从包含 Index 的路径进入。当前源码就是这种正常入口。若未来支持设置页被深链直接打开,就不能假定 back() 后一定存在 Index,需要额外定义兜底目标;当前项目没有该深链能力。
七、题库卡片集中决定详情页,而不是让首页判断
首页和题库列表都会复用 BankCard。不同题库可能有专属详情页,其他题库走通用详情页。源码把映射放在卡片组件内部:
private detailPageUrl(): string {
switch (this.bank.id) {
case 'b_sichuan':
return 'pages/SichuanBankPage'
case 'b_yue':
return 'pages/YueBankPage'
case 'b_northeast':
return 'pages/NortheastBankPage'
case 'b_shanghai':
return 'pages/ShanghaiBankPage'
case 'b_minnan':
return 'pages/MinnanBankPage'
case 'b_hakka':
return 'pages/HakkaBankPage'
default:
return 'pages/BankDetailPage'
}
}
点击卡片时,横向布局和紧凑布局都走同一规则:
.onClick(() => {
router.pushUrl({
url: this.detailPageUrl(),
params: {
bankId: this.bank.id
}
})
})
这避免了首页、题库列表各自维护一份题库 ID 到页面 URL 的映射。BankCard 是实际发起详情导航的组件,因此它拥有“这个题库应该打开哪个详情页”的本地规则。
不过,随着专属页面继续增加,switch 会变长。可落地的收口方向是把映射提取为只读配置,并在没有命中时仍返回 BankDetailPage。无论是否抽取,都要保留默认分支,否则新增题库但忘记配置专属页面时,点击入口会失效。
参数 bankId 始终随路由发送,即使专属详情页看起来已经能从页面名推断题库。这样接收页仍以显式数据为依据,避免把业务 ID隐藏在 URL 命名规则里。
八、SearchPage 对可选参数做温和降级
首页可以有两种方式进入搜索页:
router.pushUrl({ url: 'pages/SearchPage' })
或者:
router.pushUrl({
url: 'pages/SearchPage',
params: {
categoryType: cat.type,
categoryName: cat.name
}
})
因此搜索页的参数接口把两个字段都声明为可选:
interface SearchParams {
categoryType?: string
categoryName?: string
}
接收时先判断参数对象和关键字段:
aboutToAppear(): void {
const params = router.getParams() as SearchParams | undefined
if (params && params.categoryType) {
this.categoryType = params.categoryType
this.categoryName =
params.categoryName || params.categoryType
this.keyword = this.categoryName
this.doSearch()
}
}
没有参数时,页面保持普通搜索首页;有 categoryType 时,自动进入分类搜索;缺少 categoryName 时,回退使用 categoryType 作为展示和关键词。一个页面因此支持“空参数入口”和“分类入口”,且不会因为缺少可选展示名而崩溃。
这里的参数校验是轻量的存在性判断,不是运行时 Schema 校验。as SearchParams 只告诉 ArkTS 编译器期望类型,并不会自动阻止外部传入数字或未知字符串。如果项目未来有深链、跨 Ability 或外部 Want 参数,接收边界还需要显式检查 typeof、白名单和长度;当前内部固定入口下,源码只实现了可选值兜底。
九、PracticePage 区分必填参数、默认值和可解析数据
练习页参数比搜索页复杂:
interface PracticeParams {
bankId: string
chapterId?: string
mode: string
records?: string
}
bankId 和 mode 在类型上是必填,chapterId 和 records 可选。页面接收后按模式决定数据来源:
aboutToAppear(): void {
const params = router.getParams() as PracticeParams | undefined
if (params) {
this.bankId = params.bankId
this.mode = params.mode || 'chapter'
this.chapterId = params.chapterId || ''
if (this.mode === 'wrongAnalysis') {
this.loadWrongAnalysis(params)
} else if (this.mode === 'wrong') {
// 从错题记录收集题目
} else if (params.chapterId) {
this.questions = getQuestionsByChapter(
params.bankId,
params.chapterId
)
} else {
this.questions = getQuestions(params.bankId)
}
}
}
源码提供了两个实际兜底:
mode为空时回退为'chapter';chapterId缺失时回退为空字符串,并走普通题库加载。
错题分析模式还要解析序列化记录:
private loadWrongAnalysis(params: PracticeParams): void {
let examRecords: AnswerRecord[] = []
if (params.records) {
try {
examRecords =
JSON.parse(params.records) as AnswerRecord[]
} catch (_) {
examRecords = []
}
}
this.records = examRecords.filter(record => !record.correct)
}
JSON 解析失败后使用空数组,避免错误文本直接造成页面异常。这是参数边界里非常实用的一层保护。
同时,当前代码只判断 params 是否存在,没有显式拒绝空 bankId 或未知 mode。内部入口都传入项目定义的值,所以正常流程可用;若要把它提升为统一参数校验,应增加银行 ID 是否存在、模式是否属于允许集合、records 是否真的是记录数组等检查,并在无效时显示错误状态或安全返回。
十、push、replace、back 的选择应围绕任务生命周期
句匠源码中三种操作各有真实使用场景。
pushUrl:进入可以返回的二级任务
首页进入搜索、分类、设置、统计和功能页,题库卡片进入详情,详情页进入练习,通常都希望用户能返回上一层,因此使用 pushUrl。
replaceUrl:当前页面不应留在返回栈
Splash 进入 Index:
router.replaceUrl({ url: 'pages/Index' })
考试倒计时结束后,练习页进入结果页:
router.replaceUrl({
url: 'pages/ExamResultPage',
params: {
bankId: this.bankId,
score,
total: this.questions.length,
correct: correctCount,
durationSec: this.timerSec,
records: JSON.stringify(this.records)
}
})
启动页不应该通过返回键重现,已提交的考试页也不应简单返回到仍可继续作答的旧状态。替换路由表达了“当前任务阶段已经结束”。
back:结束当前二级任务
通用顶部栏、搜索页、自定义答题页返回按钮都使用 router.back()。设置页在回退前可以先修改主 Tab 状态。

| API / 状态 | 适用问题 | 句匠实际例子 |
|---|---|---|
currentTabIndex | 同一主页层级切换 | 首页、题库、错题、收藏、我的 |
pushUrl | 新增可返回页面 | 搜索、分类、详情、设置 |
replaceUrl | 当前阶段结束,不应返回 | Splash→Index、答题→结果 |
back | 结束当前二级页 | TopBar、搜索头部、设置返回 |
params | 给新页面传最小上下文 | categoryType、bankId、mode |
选择 API 时不要只追求调用形式统一,而要统一“返回后用户应该看到什么”。
十一、如何在现有源码上收口导航常量
当前项目直接在各页面写 'pages/SearchPage'、'pages/CategoryPage' 等字符串,主 Tab 也直接写数字。文章可以基于现状提出最小收口,但不能把下面的建议说成项目已实现。
第一步可以定义路由和 Tab 常量:
export class AppRoutes {
static readonly SEARCH: string = 'pages/SearchPage'
static readonly CATEGORY: string = 'pages/CategoryPage'
static readonly PRACTICE: string = 'pages/PracticePage'
static readonly SETTINGS: string = 'pages/SettingsPage'
}
export enum MainTab {
HOME = 0,
BANK = 1,
EXAM = 2,
FAVORITE = 3,
MINE = 4
}
这一步只消除魔法字符串和数字,不引入复杂框架。调用处仍使用官方 router:
this.currentTabIndex = MainTab.BANK
router.pushUrl({
url: AppRoutes.SEARCH,
params: {
categoryType: cat.type,
categoryName: cat.name
}
})
第二步只在确有重复时封装参数构造:
interface SearchRouteParams {
categoryType?: string
categoryName?: string
}
function openCategorySearch(
categoryType: string,
categoryName: string
): void {
if (categoryType.trim().length === 0) {
return
}
router.pushUrl({
url: AppRoutes.SEARCH,
params: {
categoryType,
categoryName
} as SearchRouteParams
})
}
更重的 NavigationService 只有在需要统一日志、防重复点击、鉴权或错误上报时才值得引入。当前项目规模下,常量、类型和接收端校验已经能解决大部分维护风险。
十二、参数校验应放在接收端最后把关
发送端可以保证项目内部调用正确,但接收端仍是页面的信任边界。尤其是同一页面存在多个入口时,不能只依赖某一个调用方。
以练习页为例,可把模式限制为源码真实使用的集合:
type PracticeMode =
| 'chapter'
| 'random'
| 'exam'
| 'wrong'
| 'wrongAnalysis'
function isPracticeMode(value: string): boolean {
return value === 'chapter' ||
value === 'random' ||
value === 'exam' ||
value === 'wrong' ||
value === 'wrongAnalysis'
}
接收时再处理:
const params =
router.getParams() as PracticeParams | undefined
if (!params || typeof params.bankId !== 'string' ||
params.bankId.trim().length === 0) {
this.questions = []
return
}
this.bankId = params.bankId
this.mode = isPracticeMode(params.mode)
? params.mode
: 'chapter'
这段是基于当前参数模型给出的增强建议,不是对现有文件的逐字复刻。它补足了 as PracticeParams 不提供运行时校验的问题。
参数校验要与页面状态结合:
- 必填 ID 无效:显示错误或空状态,并提供返回;
- 可选展示名缺失:使用业务类型或默认文案;
- 模式未知:回退到安全模式;
- JSON 无法解析:使用空数组,不继续信任其结构;
- 路由目标未注册:在开发阶段通过页面清单和测试发现。
直接 router.back() 虽然简洁,但如果页面可能作为外部入口,参数无效后的返回目标也要定义。当前句匠没有对外深链,因此本文只把它列为未来边界,不宣称已经支持。
十三、导航验证要覆盖路径组合
单独点击每个按钮并不足以证明导航稳定。更有效的是按路径组合验证:
| 测试路径 | 预期结果 |
|---|---|
| Splash→Index→题库 Tab | 不返回 Splash,题库不新增路由记录 |
| 首页→搜索→返回 | 回到原首页与原主 Tab |
| 首页题型→带参数搜索 | 自动带入分类并执行搜索 |
| 首页题型→缺少 categoryName | 使用 categoryType 作为显示值 |
| 首页→设置→收藏数据入口 | 先退出设置,再显示收藏主 Tab 与目标子 Tab |
| 题库卡片→专属详情→返回 | 返回原题库列表或首页位置 |
| 未映射题库→通用详情 | 命中 BankDetailPage 默认分支 |
| 练习→提交考试→结果 | 使用 replace,不能回到可继续提交的旧答题状态 |
| 错题分析 records 为非法 JSON | 页面不崩溃,记录回退为空数组 |
| 连续快速点击同一入口 | 不出现明显重复页面或多次任务 |
真实设备还要验证系统返回手势、顶部返回按钮、横竖屏和小窗口。Index 的底部导航已经考虑系统底部避让区,TopBar 读取顶部安全区;导航行为正确但按钮被系统区域遮挡,同样属于不可用。
可以在开发日志中记录目标 URL、参数关键字段和导航结果,但不能打印用户隐私或完整学习内容。对于 records 这种序列化答题数据,更适合记录数量和解析结果,而不是把全部内容写入日志。
十四、常见导航故障与排查顺序
| 现象 | 优先检查 | 根因方向 | 修复方式 |
|---|---|---|---|
| 主 Tab 返回键逐页回退 | 是否误用 pushUrl | 一级页面进入路由栈 | 改为共享索引切换 |
| 返回后主页 Tab 不对 | currentTabIndex 写入时机 | back 前未设置目标状态 | 先写状态,再 back |
| 点击题库进入错误详情 | detailPageUrl() | ID 映射遗漏或写错 | 核对映射并保留默认页 |
| 分类搜索没有自动执行 | router.getParams() | categoryType 缺失或名称不一致 | 对齐字段并做可选兜底 |
| 练习页空白 | bankId、mode、题目集合 | 参数缺失或模式未知 | 接收端校验并显示错误态 |
| 错题分析闪退 | JSON.parse(records) | 非法 JSON 未捕获 | try/catch 后使用空数组 |
| 结果页返回到已提交答题 | 跳转方式 | 使用 push 保留旧答题页 | 任务完成时使用 replace |
| 返回按钮标题错位 | TopBar 两侧宽度 | 缺少占位导致标题不居中 | 左右保持等宽占位 |
| 快速点击产生重复页面 | 点击节流与当前 URL | 同一入口连续 push | 增加短时防重复或状态锁 |
排查时先确认导航类型,再检查目标路径,最后检查参数。很多“页面数据为空”不是业务查询问题,而是入口根本没有传 bankId;很多“返回异常”也不是系统返回失效,而是上一层页面被错误地替换或重复压栈。
十五、源码边界与可复用结论
从句匠源码能够得到的导航方法不是“所有跳转都封装成一个函数”,而是按层级统一语义:
- 主 Tab 由
Index统一持有,使用currentTabIndex切换; - 首页复用卡片只触发调用方传入的动作,不自行猜路由;
- 二级任务页使用
pushUrl,返回使用back; - 不应回到的阶段使用
replaceUrl; - 带参数页面在接收端提供默认值和解析兜底;
- 设置页通过“写共享状态再 back”回到指定主区域;
- 题库详情映射集中在实际发起导航的
BankCard; - 路由常量、Tab 枚举和更严格校验属于可落地优化,并非当前已完成能力。
在 HarmonyOS 5.0 及以上 ArkTS 项目里,导航可靠性取决于三件事:页面层级是否明确、返回栈是否符合任务生命周期、参数是否在接收边界被检查。句匠当前源码已经具备“主页状态切换 + 二级路由栈 + 部分参数兜底”的基础结构。继续演进时,先收口字符串和数字,再补接收端校验;只有出现真实的日志、鉴权或防重需求时,才增加导航服务层。
AI 辅助声明
本文由 AI 辅助生成和整理,内容已根据 com.jiaweikang.one18 项目中的 Index.ets、HomePage.ets、SearchPage.ets、PracticePage.ets、SettingsPage.ets、BankCard.ets 与 TopBar.ets 逐项复核;建议性代码已明确标注,能力描述仅覆盖源码中可验证的导航与参数处理范围。
更多推荐


所有评论(0)