Analog
On this page

路由

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

定义路由

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

路由主要由以下 5 种:

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

索引页路由

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

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

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

静态路由

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

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

@Component({
  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 路由。

@Component({
  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
export const appConfig: ApplicationConfig = {
  providers: [
    provideFileRouter(withComponentInputBinding()),
    // other providers
  ],
};

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

// src/app/pages/products/[productId].page.ts
@Component({
  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 的布局页。

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

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

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

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

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

@Component({
  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 页面。

export const routeMeta: RouteMeta = {
  title: 'Page Not Found',
  canActivate: [
    () => {
      const response = injectResponse();
      if (import.meta.env.SSR && response) {
        response.statusCode = 404;
        response.end();
      }
      return true;
    },
  ],
};

@Component({
  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)
/products products/(product-list).page.ts (layout: products.page.ts)
/products/1 products/[productId].page.ts (layout: products.page.ts)
/products/1/edit products/[productId].edit.page.ts (layout: products.page.ts)
/unknown-url [...not-found].md

提供额外路由

除了通过文件系统发现的路由外,还可以手动添加路由。使用带有路由数组的 withExtraRoutes,将其预置到发现的路由数组中。所有路由将合并到一个数组中。

const customRoutes: Routes = [
  {
    path: 'custom',
    loadComponent: () =>
      import('./custom-component').then((m) => m.CustomComponent),
  },
];

export const appConfig: ApplicationConfig = {
  providers: [provideFileRouter(withExtraRoutes(customRoutes))],
};

可视化和调试路由

在构建应用程序页面时,可视化查看基于文件系统结构的路由会很有帮助。你可以使用 withDebugRoutes() 函数提供一个调试路由,显示应用程序的页面和布局。

app.config.ts 中使用 withDebugRoutes 函数:

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

在浏览器中导航到 __analog/routes URL 以查看路由表。

debug routes page

提供额外路由

除了通过文件系统发现的路由外,还可以手动添加路由。使用带有路由数组的 withExtraRoutes,将其预置到发现的路由数组中。所有路由将合并到一个数组中。

const customRoutes: Routes = [
  {
    path: 'custom',
    loadComponent: () =>
      import('./custom-component').then((m) => m.CustomComponent),
  },
];

export const appConfig: ApplicationConfig = {
  providers: [provideFileRouter(withExtraRoutes(customRoutes))],
};

可视化和调试路由

在构建应用程序页面时,可视化查看基于文件系统结构的路由会很有帮助。你可以使用 withDebugRoutes() 函数提供一个调试路由,显示应用程序的页面和布局。

app.config.ts 中使用 withDebugRoutes 函数:

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

在浏览器中导航到 __analog/routes URL 以查看路由表。

debug routes page