SKILL.md
readonly只读
name
angular-routing
description
在 Angular v20+ 应用中实现路由,支持懒加载、函数式守卫、解析器和路由参数。用于导航设置、受保护路由、基于路由的数据加载和嵌套路由。触发条件包括路由配置、添加认证守卫、实现懒加载或使用信号读取路由参数。
Angular 路由
在 Angular v20+ 中配置路由,支持懒加载、函数式守卫和基于信号的路由参数。
基本设置
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: Home },
{ path: 'about', component: About },
{ path: '**', component: NotFound },
];
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
],
};
// app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-root',
imports: [RouterOutlet, RouterLink, RouterLinkActive],
template: `
<nav>
<a routerLink="/home" routerLinkActive="active">首页</a>
<a routerLink="/about" routerLinkActive="active">关于</a>
</nav>
<router-outlet />
`,
})
export class App {}
懒加载
按需加载功能模块:
// app.routes.ts
export const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: Home },
// 懒加载整个功能模块
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes').then(m => m.adminRoutes),
},
// 懒加载单个组件
{
path: 'settings',
loadComponent: () => import('./settings/settings.component').then(m => m.Settings),
},
];
// admin/admin.routes.ts
export const adminRoutes: Routes = [
{ path: '', component: AdminDashboard },
{ path: 'users', component: AdminUsers },
{ path: 'settings', component: AdminSettings },
];
路由参数
使用信号输入(推荐)
// 路由配置
{ path: 'users/:id', component: UserDetail }
// 组件 - 使用 input() 获取路由参数
import { Component, input, computed } from '@angular/core';
@Component({
selector: 'app-user-detail',
template: `
<h1>用户 {{ id() }}</h1>
`,
})
export class UserDetail {
// 路由参数作为信号输入
id = input.required<string>();
// 基于路由参数的计算值
userId = computed(() => parseInt(this.id(), 10));
}
启用 withComponentInputBinding():
// app.config.ts
import { provideRouter, withComponentInputBinding } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withComponentInputBinding()),
],
};
查询参数
// 路由: /search?q=angular&page=1
@Component({...})
export class Search {
// 查询参数作为输入
q = input<string>('');
page = input<string>('1');
currentPage = computed(() => parseInt(this.page(), 10));
}
使用 ActivatedRoute(备选)
import { Component, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
import { map } from 'rxjs';
@Component({...})
export class UserDetail {
private route = inject(ActivatedRoute);
// 将路由参数转换为信号
id = toSignal(
this.route.paramMap.pipe(map(params => params.get('id'))),
{ initialValue: null }
);
// 查询参数
query = toSignal(
this.route.queryParamMap.pipe(map(params => params.get('q'))),
{ initialValue: '' }
);
}
函数式守卫
认证守卫
// guards/auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
export const authGuard: CanActivateFn = (route, state) => {
const authService = inject(Auth);
const router = inject(Router);
if (authService.isAuthenticated()) {
return true;
}
// 重定向到登录页,携带返回 URL
return router.createUrlTree(['/login'], {
queryParams: { returnUrl: state.url },
});
};
// 在路由中使用
{
path: 'dashboard',
component: Dashboard,
canActivate: [authGuard],
}
角色守卫
export const roleGuard = (allowedRoles: string[]): CanActivateFn => {
return (route, state) => {
const authService = inject(Auth);
const router = inject(Router);
const userRole = authService.currentUser()?.role;
if (userRole && allowedRoles.includes(userRole)) {
return true;
}
return router.createUrlTree(['/unauthorized']);
};
};
// 使用
{
path: 'admin',
component: Admin,
canActivate: [authGuard, roleGuard(['admin', 'superadmin'])],
}
CanDeactivate 守卫
export interface CanDeactivate {
canDeactivate: () => boolean | Promise<boolean>;
}
export const unsavedChangesGuard: CanDeactivateFn<CanDeactivate> = (component) => {
if (component.canDeactivate()) {
return true;
}
return confirm('您有未保存的更改。确定离开吗?');
};
// 组件实现
@Component({...})
export class Edit implements CanDeactivate {
form = inject(FormBuilder).group({...});
canDeactivate(): boolean {
return !this.form.dirty;
}
}
// 路由
{
path: 'edit/:id',
component: Edit,
canDeactivate: [unsavedChangesGuard],
}
解析器
在路由激活前预取数据:
// resolvers/user.resolver.ts
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
export const userResolver: ResolveFn<User> = (route) => {
const userService = inject(User);
const id = route.paramMap.get('id')!;
return userService.getById(id);
};
// 路由配置
{
path: 'users/:id',
component: UserDetail,
resolve: { user: userResolver },
}
// 组件 - 通过输入访问解析后的数据
@Component({...})
export class UserDetail {
user = input.required<User>();
}
嵌套路由
// 父路由带子路由
export const routes: Routes = [
{
path: 'products',
component: ProductsLayout,
children: [
{ path: '', component: ProductList },
{ path: ':id', component: ProductDetail },
{ path: ':id/edit', component: ProductEdit },
],
},
];
// ProductsLayout
@Component({
imports: [RouterOutlet],
template: `
<h1>产品</h1>
<router-outlet /> <!-- 子路由在此渲染 -->
`,
})
export class ProductsLayout {}
编程式导航
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
@Component({...})
export class Product {
private router = inject(Router);
// 导航到路由
goToProducts() {
this.router.navigate(['/products']);
}
// 带参数导航
goToProduct(id: string) {
this.router.navigate(['/products', id]);
}
// 带查询参数导航
search(query: string) {
this.router.navigate(['/search'], {
queryParams: { q: query, page: 1 },
});
}
// 相对于当前路由导航
goToEdit() {
this.router.navigate(['edit'], { relativeTo: this.route });
}
// 替换当前历史记录
replaceUrl() {
this.router.navigate(['/new-page'], { replaceUrl: true });
}
}
路由数据
// 静态路由数据
{
path: 'admin',
component: Admin,
data: {
title: '管理后台',
roles: ['admin'],
},
}
// 在组件中访问
@Component({...})
export class AdminCmpt {
title = input<string>(); // 来自路由数据
roles = input<string[]>(); // 来自路由数据
}
// 或通过 ActivatedRoute
private route = inject(ActivatedRoute);
data = toSignal(this.route.data);
路由事件
import { Router, NavigationStart, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs';
@Component({...})
export class AppMain {
private router = inject(Router);
isNavigating = signal(false);
constructor() {
this.router.events.pipe(
filter(e => e instanceof NavigationStart || e instanceof NavigationEnd)
).subscribe(event => {
this.isNavigating.set(event instanceof NavigationStart);
});
}
}
有关高级模式,请参阅 references/routing-patterns.md。






