NativeScript 导航路由 #
导航概述 #
NativeScript 提供了灵活的导航系统,支持多种导航模式。
text
┌─────────────────────────────────────────────────────────────┐
│ 导航模式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 页面导航 Frame-based Navigation │
│ ├── 基本导航 navigate / goBack │
│ ├── 参数传递 context / navigationContext │
│ └── 导航动画 transitions │
│ │
│ 标签导航 Tab Navigation │
│ ├── TabView 标签页 │
│ └── BottomNavigation 底部导航 │
│ │
│ 模态导航 Modal Navigation │
│ ├── 模态页面 showModal │
│ └── 关闭模态 closeModal │
│ │
└─────────────────────────────────────────────────────────────┘
Frame 导航 #
基本导航 #
typescript
import { Frame } from '@nativescript/core';
// 导航到新页面
Frame.topmost().navigate('pages/detail/detail-page');
// 返回上一页
Frame.topmost().goBack();
// 返回到根页面
Frame.topmost().goBack();
导航配置 #
typescript
Frame.topmost().navigate({
moduleName: 'pages/detail/detail-page',
context: {
id: 123,
name: 'John'
},
animated: true,
transition: {
name: 'slide',
duration: 300,
curve: 'easeInOut'
},
clearHistory: false
});
导航选项 #
| 选项 | 类型 | 说明 |
|---|---|---|
| moduleName | string | 页面模块路径 |
| context | any | 传递的参数 |
| animated | boolean | 是否使用动画 |
| transition | object | 过渡动画配置 |
| clearHistory | boolean | 是否清除导航历史 |
| backstackVisible | boolean | 是否在返回栈中可见 |
参数传递 #
发送参数 #
typescript
// 发送参数
Frame.topmost().navigate({
moduleName: 'pages/detail/detail-page',
context: {
productId: 123,
productName: 'iPhone 15'
}
});
接收参数 #
typescript
// detail-page.ts
import { Page, NavigatedData } from '@nativescript/core';
let page: Page;
let productId: number;
export function onNavigatedTo(args: NavigatedData) {
page = args.object as Page;
if (args.isBackNavigation) {
return;
}
const context = page.navigationContext;
productId = context.productId;
console.log('Product ID:', productId);
}
返回数据 #
typescript
// 发送页面
export function onSelectItem() {
Frame.topmost().navigate({
moduleName: 'pages/item-list/item-list-page',
context: {
callback: (selectedItem) => {
console.log('Selected:', selectedItem);
}
}
});
}
// 选择页面
export function onItemTap(args) {
const page = args.object.page;
const context = page.navigationContext;
if (context.callback) {
context.callback(selectedItem);
}
Frame.topmost().goBack();
}
导航动画 #
内置动画 #
typescript
// 滑动动画
Frame.topmost().navigate({
moduleName: 'pages/detail/detail-page',
transition: {
name: 'slide'
}
});
// 淡入淡出
Frame.topmost().navigate({
moduleName: 'pages/detail/detail-page',
transition: {
name: 'fade'
}
});
// 翻转动画
Frame.topmost().navigate({
moduleName: 'pages/detail/detail-page',
transition: {
name: 'flip'
}
});
// 无动画
Frame.topmost().navigate({
moduleName: 'pages/detail/detail-page',
animated: false
});
动画类型 #
| 名称 | 说明 |
|---|---|
| slide | 滑动(默认) |
| fade | 淡入淡出 |
| flip | 翻转 |
| slideLeft | 向左滑动 |
| slideRight | 向右滑动 |
| slideTop | 向上滑动 |
| slideBottom | 向下滑动 |
| explode | 爆炸效果 |
自定义动画 #
typescript
import { Transition, SlideTransition } from '@nativescript/core';
// 自定义过渡
const customTransition: Transition = {
name: 'custom',
duration: 500,
curve: 'spring',
createAndroidTransition: () => {
// Android 自定义过渡
},
createIOSTransition: () => {
// iOS 自定义过渡
}
};
Frame.topmost().navigate({
moduleName: 'pages/detail/detail-page',
transition: customTransition
});
页面生命周期 #
生命周期事件 #
xml
<Page navigatingTo="onNavigatingTo"
navigatedTo="onNavigatedTo"
navigatingFrom="onNavigatingFrom"
navigatedFrom="onNavigatedFrom"
unloaded="onUnloaded">
<!-- 页面内容 -->
</Page>
typescript
export function onNavigatingTo(args) {
console.log('即将导航到此页面');
}
export function onNavigatedTo(args) {
console.log('已导航到此页面');
if (args.isBackNavigation) {
console.log('从其他页面返回');
}
}
export function onNavigatingFrom(args) {
console.log('即将离开此页面');
}
export function onNavigatedFrom(args) {
console.log('已离开此页面');
if (args.isBackNavigation) {
console.log('返回上一页');
}
}
export function onUnloaded(args) {
console.log('页面已卸载');
}
生命周期流程 #
text
┌─────────────────────────────────────────────────────────────┐
│ 页面生命周期 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 导航到页面: │
│ navigatingTo → navigatedTo │
│ │
│ 离开页面: │
│ navigatingFrom → navigatedFrom │
│ │
│ 返回页面: │
│ navigatingTo (isBackNavigation=true) │
│ → navigatedTo (isBackNavigation=true) │
│ │
│ 页面销毁: │
│ unloaded │
│ │
└─────────────────────────────────────────────────────────────┘
模态导航 #
显示模态页面 #
typescript
import { Page } from '@nativescript/core';
// 显示模态页面
const modalPage = 'pages/modal/modal-page';
const context = { title: 'Modal Title' };
const result = await page.showModal(modalPage, {
context: context,
closeCallback: (result) => {
console.log('Modal result:', result);
},
fullscreen: true,
animated: true,
stretched: false
});
模态页面 #
xml
<!-- modal-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
class="modal-page">
<GridLayout rows="auto, *, auto">
<Label text="{{ title }}" row="0" class="title" />
<StackLayout row="1">
<!-- 模态内容 -->
</StackLayout>
<Button text="Close" tap="onClose" row="2" />
</GridLayout>
</Page>
typescript
// modal-page.ts
import { Page } from '@nativescript/core';
let page: Page;
let closeCallback: Function;
export function onNavigatedTo(args) {
page = args.object as Page;
const context = page.navigationContext;
closeCallback = context.closeCallback;
}
export function onClose() {
closeCallback({ result: 'success' });
}
模态选项 #
| 选项 | 类型 | 说明 |
|---|---|---|
| context | any | 传递的参数 |
| closeCallback | function | 关闭回调 |
| fullscreen | boolean | 是否全屏 |
| animated | boolean | 是否使用动画 |
| stretched | boolean | 是否拉伸 |
TabView 导航 #
基本用法 #
xml
<TabView selectedIndex="{{ selectedIndex }}"
tabTextColor="#999"
selectedTabTextColor="#3498db"
tabBackgroundColor="#f5f5f5"
selectedIndexChanged="onTabChanged">
<TabView.items>
<TabViewItem title="Home" iconSource="res://home">
<TabViewItem.view>
<GridLayout>
<Label text="Home Page" />
</GridLayout>
</TabViewItem.view>
</TabViewItem>
<TabViewItem title="Search" iconSource="res://search">
<TabViewItem.view>
<GridLayout>
<Label text="Search Page" />
</GridLayout>
</TabViewItem.view>
</TabViewItem>
<TabViewItem title="Profile" iconSource="res://user">
<TabViewItem.view>
<GridLayout>
<Label text="Profile Page" />
</GridLayout>
</TabViewItem.view>
</TabViewItem>
</TabView.items>
</TabView>
TabView 属性 #
| 属性 | 说明 |
|---|---|
| selectedIndex | 当前选中的标签索引 |
| tabTextColor | 标签文本颜色 |
| selectedTabTextColor | 选中标签文本颜色 |
| tabBackgroundColor | 标签栏背景色 |
| androidTabsPosition | Android 标签位置 |
BottomNavigation #
Material Design 风格的底部导航:
xml
<BottomNavigation selectedIndex="{{ selectedIndex }}">
<BottomNavigation.items>
<TabStripItem title="Home">
<TabStripItem.iconSource>
<FontIconSource icon="home" fontSize="24" />
</TabStripItem.iconSource>
</TabStripItem>
<TabStripItem title="Search">
<TabStripItem.iconSource>
<FontIconSource icon="search" fontSize="24" />
</TabStripItem.iconSource>
</TabStripItem>
<TabStripItem title="Profile">
<TabStripItem.iconSource>
<FontIconSource icon="user" fontSize="24" />
</TabStripItem.iconSource>
</TabStripItem>
</BottomNavigation.items>
<BottomNavigation.content>
<GridLayout>
<Label text="{{ 'Page ' + selectedIndex }}" />
</GridLayout>
</BottomNavigation.content>
</BottomNavigation>
路由守卫 #
Angular 路由守卫 #
typescript
// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router
) {}
canActivate(): boolean {
if (this.authService.isLoggedIn()) {
return true;
}
this.router.navigate(['/login']);
return false;
}
}
typescript
// app.routing.ts
import { NgModule } from '@angular/core';
import { Routes } from '@angular/router';
import { NativeScriptRouterModule } from '@nativescript/angular';
import { AuthGuard } from './auth.guard';
const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{
path: 'profile',
component: ProfileComponent,
canActivate: [AuthGuard]
},
{ path: 'login', component: LoginComponent }
];
@NgModule({
imports: [NativeScriptRouterModule.forRoot(routes)],
exports: [NativeScriptRouterModule]
})
export class AppRoutingModule {}
Vue 路由守卫 #
typescript
// router/index.ts
import Vue from 'nativescript-vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{ path: '/login', component: Login },
{
path: '/profile',
component: Profile,
meta: { requiresAuth: true }
}
]
});
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isLoggedIn()) {
next('/login');
} else {
next();
}
});
export default router;
导航最佳实践 #
页面命名规范 #
text
pages/
├── home/
│ ├── home-page.xml
│ ├── home-page.ts
│ └── home-page.css
├── detail/
│ ├── detail-page.xml
│ ├── detail-page.ts
│ └── detail-page.css
└── settings/
├── settings-page.xml
├── settings-page.ts
└── settings-page.css
导航服务 #
typescript
// services/navigation.service.ts
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class NavigationService {
constructor(private router: Router) {}
goToDetail(id: number) {
this.router.navigate(['/detail', id]);
}
goToProfile() {
this.router.navigate(['/profile']);
}
goBack() {
this.router.back();
}
}
避免内存泄漏 #
typescript
import { Page, Application } from '@nativescript/core';
export class DetailComponent implements OnInit, OnDestroy {
private page: Page;
ngOnInit() {
this.page = Application.getActivePage();
this.page.on(Page.navigatedFromEvent, this.onNavigatedFrom, this);
}
ngOnDestroy() {
this.page.off(Page.navigatedFromEvent, this.onNavigatedFrom, this);
}
onNavigatedFrom() {
// 清理资源
}
}
下一步 #
现在你已经掌握了导航系统,接下来学习 数据绑定,了解数据与视图的绑定方式!
最后更新:2026-03-29