跳到主要内容

路由

Analog 在 Angular 路由之上支持基于文件系统的路由。

定义路由

路由通过 src/app/pages 目录的文件和子目录来定义。只有文件名以 .page.ts 结尾的文件会被记录并创建路由。

信息

路由组件必须被定义为 default 导出并且所有的路由组件都是懒加载

路由主要由以下 5 种:

这些路由可以通过不同的方式组合来生成导航 URL。

笔记

除了以上 5 种主要路由以外,Analog 还支持 重定向路由内容路由

索引页路由

索引页路由是通过在文件名中加括号的方式定义的。

以下文件 src/app/pages/(home).page.ts 定义了一个 / 路由。

import { Component } from '@angular/core';

@Component({
standalone: true,
template: ` <h2>Welcome</h2> `,
})
export default class HomePageComponent {}
提示

索引页路由也可以通过文件名 index.page.ts 来定义。

静态路由

静态路由是使用文件名作为路由路径来定义的。

以下文件 src/app/pages/about.page.ts 中定义了一个 /about 路由.

import { Component } from '@angular/core';

@Component({
standalone: true,
template: `
<h2>Hello Analog</h2>

Analog is a meta-framework on top of Angular.
`,
})
export default class AboutPageComponent {}

定义多层静态路由有以下两种方式:

  1. 通过多层目录路由文件来实现 - src/app/pages/about/team.page.ts 定义了 /about/team 路由。
  2. 通过路由文件的.分隔符定义 - src/app/pages/about.team.page.ts 同样定义了 /about/team 路由。

路由组

路由文件可以通过放置在一个名字括号的目录里实现路由的分组。

src/
└── app/
└── pages/
└── (auth)/
├── login.page.ts
└── signup.page.ts

以上例子定义了 /login/signup 路由,注意:(auth)不会成为路由 URL 的一部分。

动态路由

动态路由通过文件名中的方括号[]表示。路由的参数会从路由的地址中抽取。

以下的例子中 src/app/pages/products/[productId].page.ts 定义了 /products/:productId 路由。

import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { map } from 'rxjs';

@Component({
standalone: true,
imports: [AsyncPipe],
template: `
<h2>Product Details</h2>

ID: {{ productId$ | async }}
`,
})
export default class ProductDetailsPageComponent {
private readonly route = inject(ActivatedRoute);

readonly productId$ = this.route.paramMap.pipe(
map((params) => params.get('productId'))
);
}

动态路由也可以通过文件名中的.分隔符来定义 - src/app/pages/products.[productId].page.ts 同样定义了 /products/:productId 路由。

使用路由组件的输入绑定

如果你使用了 Angular 路由的withComponentInputBinding()功能,你就可以使用Input装饰器来通过参数名获取路由参数。

首先,在provideFileRouter()函数参数中添加withComponentInputBinding()

// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideFileRouter } from '@analogjs/router';
import { withComponentInputBinding } from '@angular/router';

export const appConfig: ApplicationConfig = {
providers: [
provideFileRouter(withComponentInputBinding()),
// other providers
],
};

然后,通过 input 的方式获取路由参数

// src/app/pages/products/[productId].page.ts
import { Component, Input } from '@angular/core';

@Component({
standalone: true,
template: `
<h2>Product Details</h2>

ID: {{ productId }}
`,
})
export default class ProductDetailsPageComponent {
@Input() productId: string;
}

布局路由

布局路由是通过一个父级文件加同名目录来定义的。

下面的文档结构定义了一个布局路由。

src/
└── app/
└── pages/
├── products/
│ ├── [productId].page.ts
│ └── (products-list).page.ts
└── products.page.ts

它定义了两个共享布局的路由:

  • /products
  • /products/:productId

父级文件 src/app/pages/products.page.ts 是一个包含了 router outlet 的布局页。

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
standalone: true,
imports: [RouterOutlet],
template: `
<h2>Products</h2>

<router-outlet></router-outlet>
`,
})
export default class ProductsComponent {}

下一级的 src/app/pages/products/(products-list).page.ts 文件包含了 /products 列表页。

import { Component } from '@angular/core';

@Component({
standalone: true,
template: ` <h2>Products List</h2> `,
})
export default class ProductsListComponent {}

再下一级的 src/app/pages/products/[productId].page.ts 文件则包含了 /products/:productId 详情页。

import { Component, inject } from '@angular/core';
import { AsyncPipe, JsonPipe } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { map } from 'rxjs';

@Component({
standalone: true,
imports: [AsyncPipe, JsonPipe],
template: `
<h2>Product Details</h2>

ID: {{ productId$ | async }}
`,
})
export default class ProductDetailsPageComponent {
private readonly route = inject(ActivatedRoute);

readonly productId$ = this.route.paramMap.pipe(
map((params) => params.get('productId'))
);
}

隐式布局路由

布局路由同样支持隐式路由,即不添加额外路由项。

src/
└── app/
└── pages/
├── (auth)/
│ ├── login.page.ts
│ └── signup.page.ts
└── (auth).page.ts

上面的例子定义了共享布局的 /login/signup 路由。父级文件 src/app/pages/(auth).page.ts 是一个包含 router outlet 的布局页。

Catch-all 路由

Catch-all 路由通过一个包含[]并且以...为开头的文件来定义

下面在 src/app/pages/[...page-not-found].page.ts 里定义了一个通配符**路由,通常用于展示 404 页面。

import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';

@Component({
standalone: true,
imports: [RouterLink],
template: `
<h2>Page Not Found</h2>

<a routerLink="/">Go Back Home</a>
`,
})
export default class PageNotFoundComponent {}

Catch-all 路由同样支持多层子路由。

多种路由的组合

以下的目录结构:

src/
└── app/
└── pages/
├── (auth)/
│ ├── login.page.ts
│ └── signup.page.ts
├── (marketing)/
│ ├── about.md
│ └── contact.md
├── products/
│ ├── (product-list).page.ts
│ ├── [productId].edit.page.ts
│ └── [productId].page.ts
├── (auth).page.ts
├── (home).page.ts
├── [...not-found].md
└── products.page.ts

将基于文件的路由生成如下路径:

路径页面
/(home).page.ts
/about(marketing)/about.md
/contact(marketing)/contact.md
/login(auth)/login.page.ts (layout: (auth).page.ts)
/signup(auth)/signup.page.ts (layout: (auth).page.ts)
/productsproducts/(product-list).page.ts (layout: products.page.ts)
/products/1products/[productId].page.ts (layout: products.page.ts)
/products/1/editproducts/[productId].edit.page.ts (layout: products.page.ts)
/unknown-url[...not-found].md