Angular路由


动机

路由至关重要。许多现代Web应用程序为一个页面托管了太多信息。用户不必滚动浏览整个应用程序的内容。应用程序需要将自身拆分为可区分的部分。

用户优先考虑必要的信息。路由帮助他们找到包含此类信息的应用程序部分。对其他用户有用的任何其他信息可能存在于完全独立的路由上。通过路由,两个用户都可以快速找到所需内容。不相关的细节在不相关的路线背后隐藏。

路由优于排序和限制对应用程序数据的访问。绝不应向未经授权的用户显示敏感数据。在每条路线之间,应用程序可能会介入。它可以检查用户的会话以进行身份​​验证。该检查确定如果它应该呈现的路线呈现什么。路由为开发人员提供了在继续之前验证用户的绝佳机会。

创建路线列表也可以促进组织。在开发方面,它使开发人员在可区分的部分进行思考。用户也可以从中受益,但在浏览应用程序代码时更多的是开发人员。程序化路由器列表描绘了应用程序前端的精确模型。

至于Angular,路由在框架内占用了自己的整个库。所有现代前端框架都支持路由,而Angular也不例外。使用散列或位置路由从客户端进行路由。两种样式都允许客户端管理自己的路由。在初始请求之后,不需要服务器的其他帮助。

Web浏览器很少使用客户端路由刷新。尽管没有刷新,但书签,历史记录和地址栏等Web浏览器实用程序仍然有效。这使得流畅的路由体验不会弄乱浏览器。在路由到不同页面时,不再有重载页面重新加载。

Angular在用于路由的核心技术上增加了一层抽象。本文旨在解释这种抽象。 Angular中存在两种路由策略:路径位置和哈希。本文重点介绍路径位置策略,因为它是默认选项。

此外,路径位置可能会在Angular Universal完全发布后弃用散列路由。无论如何,这两种策略的实施非常相似。学习一个学习另一个。开始的时候了!

RouterModule安装程序

路由实用程序使用@angular/router提供的RouterModule导出。它不是核心库的一部分,因为并非所有应用程序都需要路由。引入路由的最常规方式是作为自己的功能模块。

随着路由复杂性的增加,将其作为自己的模块将促进根模块的简单性。在不损害功能的情况下保持简单愚蠢构成模块的良好设计。

import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';

 import { AComponent } from '../../components/a/a.component';
 import { BComponent } from '../../components/b/b.component';

 // an array of soon-to-be routes!
 const routes: Routes = [];

 @NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
 })
 export class AppRoutingModule { }

.forRoot(...)是一个可从RouterModule类获得的类函数。该函数接受Route对象数组作为Routes.forRoot(...)配置eager-loading路由,而其替代.forChild(...)配置延迟加载。

渴望加载意味着路由从一开始就将其内容加载到应用程序中。延迟加载是按需进行的。本文的重点是热切加载。它是在应用程序中加载的默认方法。 RouterModule类定义类似于下一个代码块。

@NgModule({
  // … lots of metadata ...
 })
 export class RouterModule {
  forRoot(routes: Routes) {
    // … configuration for eagerly loaded routes …
  }

  forChild(routes: Routes) {
    // … configuration for lazily loaded routes …
  }
 }

不要担心示例省略注释的配置详细信息。现在已经有了一般性的了解。

注意AppRoutingModule如何导入RouterModule同时导出它。鉴于AppRoutingModule是一个功能模块,这是有道理的。它作为功能模块导入根模块。它将RouterModule指令,接口和服务公开给根组件树。

这解释了为什么AppRoutingModule必须导出RouterModule。它是为了根模块的底层组件树而这样做的。它需要访问这些路由实用程序!

import { BrowserModule } from '@angular/platform-browser';
 import { NgModule } from '@angular/core';

 import { AppComponent } from './app.component';
 import { AComponent } from './components/a/a.component';
 import { BComponent } from './components/b/b.component';
 import { AppRoutingModule } from './modules/app-routing/app-routing.module';

 @NgModule({
  declarations: [
    AppComponent,
    AComponent,
    BComponent
  ],
  imports: [
    AppRoutingModule, // routing feature module
    BrowserModule
  ],
  providers: [],
  bootstrap: [ AppComponent ]
 })
 export class AppModule { }

AppRoutingModule令牌从最顶层导入。它的令牌插入到根模块的imports数组中。根组件树现在可以使用RouterModule库。这包括已经提到的指令,接口和服务。非常感谢AppRoutingModule导出RouterModule!

RouterModule实用程序将为root的组件派上用场。 AppComponent的基本HTML使用一个指令: router-outlet

<!-- app.component.html -->

 <ul>
  <!-- routerLink(s) here -->
 </ul>
 <router-outlet></router-outlet>
 <!-- routed content appends here (AFTER THE ELEMENT, NOT IN IT!) -->

routerLinkrouterLink的属性指令。一旦设置了路由,它将附加到<ul></ul>每个元素。 router-outlet是一个有趣行为的组件指令。它更像是显示路由内容的标记。路由内容是导航到特定路线的结果。通常这意味着在AppRoutingModule中配置的单个组件

路由内容在<router-outlet></router-outlet>之后立即呈现。内部没有任何东西呈现出来。这并没有太大的差别。也就是说,不要指望router-outlet的行为就像路由内容的容器一样。它仅仅是将路由内容附加到文档对象模型(DOM)的标记。

基本路由

上一节建立了路由的基本设置。在实际路由发生之前,必须解决一些问题

要解决的第一个问题是此应用程序将使用哪些路由?嗯,有两个组件:AComponent和BComponent。每个人都应该有自己的路线。它们可以根据当前路径位置从AppComponent的router-outlet进行渲染。

路径位置(或路径)通过一系列斜杠( / )定义附加到网站原点的内容(例如http:// localhost:4200)。

// … same imports from before …

 const routes: Routes = [
  {
    path: 'A',
    component: AComponent
  },
  {
    path: 'B',
    component: BComponent
  }
 ];

 @NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
 })
 export class AppRoutingModule { }

http://localhost:4200/A从AppComponent的router-outlet呈现AComponent。 http://localhost:4200/B呈现BComponent。您需要一种方法来路由到这些位置而不使用地址栏。应用程序不应依赖Web浏览器的地址栏进行导航。

全局CSS(层叠样式表)补充了它下面的HTML。应用程序的路由器链接应该具有令人愉悦的外观。此CSS也适用于所有其他示例。

/* global styles.css */

 ul li {
  cursor: pointer;
  display: inline-block;
  padding: 20px;
  margin: 5px;
  background-color: whitesmoke;
  border-radius: 5px;
  border: 1px solid black;
 }

 ul li:hover {
  background-color: lightgrey;
 }
<!-- app.component.html -->

 <ul>
  <li routerLink="/A">Go to A!</li>
  <li routerLink="/B">Go to B!</li>
 </ul>
 <router-outlet></router-outlet>

这是基本的路由!单击任一routerLink元素即可路由该Web地址。它重新分配它而不刷新Web浏览器。 Angular的Router将路由地址映射到AppRoutingModule中配置的Routes 。它将地址与数组中单个Route对象的path属性相匹配。第一场比赛总是胜利,所以匹配所有路线应该位于Routes阵列的最后。

匹配所有路由可防止应用程序在无法与当前路由匹配时崩溃。这可以从用户可以在任何路线中键入的地址栏进行。为此,Angular提供了一个接受所有路由的通配符路径值** 。此路由通常呈现PageNotFoundComponent组件显示“错误404:找不到页面”。

// … PageNotFoundComponent imported along with everything else …

 const routes: Routes = [
  {
    path: 'A',
    component: AComponent
  },
  {
    path: 'B',
    component: BComponent
  },
  {
    path: '',
    redirectTo: 'A',
    pathMatch: 'full'
  },
  {
    path: '**',
    component: PageNotFoundComponent
  }
 ];

包含redirectToRoute对象使PageNotFoundComponent不会因http://localhost:4200而呈现。这是申请回家路线。要解决此问题, redirectTo将主路由重新路由到http://localhost:4200/A http://localhost:4200/A间接成为应用程序的新主路由。

pathMatch: 'full'告诉Route对象与home路由匹配( http://localhost:4200 )。它匹配空路径。

自从第一场比赛获胜以来,这两个新的Route对象就位于数组的末尾。最后一个数组元素( path: '**' )始终匹配,因此最后一个。

在继续之前还有一件值得解决的事情。用户如何知道他或她在应用程序中相对于当前路线的位置?当然可能有特定于路线的内容,但用户应该如何建立连接?应该有一些形式的突出显示应用于routerLinks。这样,用户将知道给定网页的哪条路线是活动的。

这是一个简单的解决方案。单击routerLink元素时,Angular的Router会将_焦点_分配给它。该焦点可以触发某些样式,为用户提供有用的反馈。 routerLinkActive指令可以跟踪开发人员的这个焦点。

<!-- app.component.html -->

 <ul>
  <li routerLink="/A" routerLinkActive="active">Go to A!</li>
  <li routerLink="/B" routerLinkActive="active">Go to B!</li>
 </ul>
 <router-outlet></router-outlet>

routerLinkActive的正确分配表示一串类。此示例仅描绘一个类( .active ),但可以应用任意数量的以空格分隔的类。当Router将_焦点_分配给routerLink时,以空格分隔的类将应用于host元素。当焦点移开时,类会自动删除。

/* global styles.css */

 .active {
  background-color: lightgrey !important;
 }

用户现在可以轻松识别当前路线和页面内容的重合程度。 lightgrey highlighting适用于匹配当前路由的routerLink。 !important确保突出显示覆盖内联样式。

参数化路线

路由不必完全硬编码。它们可以包含可从与Route对象相对应的组件引用的动态变量。在写入路径的路径时,这些变量被声明为参数。

路由参数是可选的或必需的,以匹配特定的Route 。这取决于路由如何写入其参数。存在两种策略:矩阵和传统参数化。

传统参数化从AppRoutingModule中配置的Routes数组开始。

const routes: Routes = [
  // … other routes …
  {
    path: 'B',
    component: BComponent
  },
  {
    path: 'B/:parameter',
    component: BComponent
  },
  // … other routes …
 ];

专注于两条BComponent路线。参数化最终将在两个路径中发生。

传统参数化发生在第二个BComponent RouteB/:parameter包含parameter参数,如下所示: 。无论如何,冒号标记参数的名称。 parameter参数是第二个BComponent Route匹配所必需的。

parameter读入传递给路径的任何值。路由到http://localhost:4200/B/randomValue将为parameter赋值randomValue 。此值可包括除另一个/之外的任何内容。例如, http://localhost:4200/B/randomValue/blahBlah不会触发第二个BComponent Route 。而PageNotFoundComponent则呈现。

BComponent可以从其组件类引用路由参数。两种参数化方法(矩阵和传统方法)在BComponent中产生相同的结果。在查看BComponent之前,请检查下面的参数化矩阵形式。

// app.component.ts

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

 @Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
 })
 export class AppComponent {
  constructor(private router: Router) { }

  routeMatrixParam(value: string) {
    if (value)
      this.router.navigate(['B', { parameter: value }]); // matrix parameter
    else
      this.router.navigate(['B']);
  }

  routeAddressParam(value: string) {
    this.router.navigate(['B', value]);
  }
 }

Angular的依赖注入系统提供了Router的实例化。这允许组件以编程方式路由。 .navigate(...)函数接受一个解析为_可路由_路径的值数组。像.navigate(['path', 'to', 'something'])解析为http://localhost:4200/path/to/something.navigate(...)在将数组规范化为_可路由_路径时添加路径分隔/标记。

第二种形式的参数化发生在routeMatrixParam(...) 。请参阅以下代码行: this.router.navigate(['B', { parameter: value }]) 。这种形式的parameter是矩阵参数。它的值是可选的第一BComponent Route匹配( /B )。该Route无论在路径参数的存在相匹配。

routeAddressParam(...)解析与http://localhost:4200/B/randomValue参数化方法匹配的路由。这种传统策略需要一个参数来匹配第二个BComponent路径( B/:parameter )。

矩阵策略涉及routeMatrixParam(...) 。在路径中有或没有矩阵参数的情况下,第一个BComponent路径仍然匹配。 parameter参数传递给BComponent,就像传统方法一样。

为了充分了解上面的代码,这里是相应的模板HTML。

// app.component.html

 <ul>
  <li routerLink="/A">Go to A!</li>
  <li>
    <input #matrixInput>
    <button (click)="routeMatrixParam(matrixInput.value)">Matrix!</button>
  </li>
  <li>
    <input #paramInput>
    <button (click)="routeAddressParam(paramInput.value)">Param!</button>
  </li>
 </ul>
 <router-outlet></router-outlet>

在模板中,值被接受为文本输入。输入将其作为参数注入路径路径。每个参数化策略(传统和矩阵)存在两组独立的盒子。将所有部分组合在一起,是时候检查BComponent组件类了。

// b.component.ts

 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, ParamMap } from '@angular/router';

 @Component({
  selector: 'app-b',
  template: `
  <p>Route param: {{ currParam }}</p>
  `
 })
 export class BComponent implements OnInit {
  currParam: string = "";

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.params.subscribe((param: ParamMap) => {
      this.currParam = param['parameter'];
    });
  }
 }

BComponent来自AppRoutingModule中两个BComponent路由中的任何一个。 ActivatedRoute实例化为一组与当前路线有关的有用信息。也就是说,导致BComponent渲染的路由。 ActivatedRoute通过针对类构造函数的依赖注入实例化。

ActivatedRoute.params.params字段返回一个Observable ,它发出路由参数。注意两种不同的参数化方法如何产生parameter参数。返回的Observable其作为ParamMap对象内的键值对发出。

在两种参数化方法之间, parameter参数解析相同。尽管采用了参数化方法,但该值仍然来自ActivatedRoute.params

地址栏区分每种方法的最终结果。矩阵参数化( Route匹配可选)产生地址: http://localhost:4200/B;parameter=randomValue 。传统参数化( Route匹配所需)产生: http://localhost:4200/B/randomValue

无论哪种方式,都会产生相同的BComponent结果。实际差异:不同的BComponent Route匹配。这完全取决于参数化策略。矩阵方法确保参数对于Route匹配是可选的。传统方法需要它们。

嵌套路由

Routes可以形成层次结构。在DOM中,这涉及一个父router-outlet渲染至少一个子router-outlet 。在地址栏中,它看起来像这样: http://localhost/parentRoutes/childRoutes 。在Routes配置中, children: []属性表示Route对象具有嵌套(子)路由。

import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';

 import { NestComponent } from '../../components/nest/nest.component';
 import { AComponent } from '../../components/nest/a/a.component';
 import { BComponent } from '../../components/nest/b/b.component';

 const routes: Routes = [
  {
    path: 'nest',
    component: NestComponent,
    children: [
      { path: 'A', component: AComponent },
      { path: 'B', component: BComponent }
    ]
  }
 ];

 @NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
 })
 export class AppRoutingModule { }
// nest.component.ts

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

 @Component({
  selector: 'app-nest',
  template: `
  <ul>
    <li routerLink="./A">Go to A!</li>
    <li routerLink="./B">Go to B!</li>
  </ul>
  <router-outlet></router-outlet>
  `
 })
 export class NestComponent { }

NestComponent呈现一个router-outlet从另一个根级渲染本身后router-outlet在AppComponent。 NestComponent模板的router-outlet可以呈现AComponent( /nest/A )或BComponent( /nest/B )。

AppRoutingModule在NestComponent的Route对象中反映了这种嵌套。 children: []字段包含一组Route对象。这些Route对象也可以在其children: []节点中嵌套路径children: []字段。然而,这可以继续多层嵌套路由。上面的例子显示了两层嵌套。

/相比,每个routerLink包含./ 。这个.确保routerLink附加到路由路径。 routerLink完全替换了路径。路由到/nest.扩展到/nest

这对于从.nest路由路由到/nest/A/nest/B非常有用。 AB构成/nest嵌套路由。路由到/A/B返回PageNotFound。 /nest必须在前面添加两条路由。

在模板中查看包含根级router-outlet的AppComponent。 AppComponent是第一层嵌套,而NestComponent是第二层。

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

 @Component({
  selector: 'app-root',
  template: `
  <ul>
    <li routerLink="/nest">Go to nested routes!</li>
    <li routerLink="/">Back out of the nested routes!</li>
  </ul>
  <router-outlet></router-outlet>
  `
 })
 export class AppComponent { }

在嵌套Route对象中, children: []包含两个嵌套路由。如前所述,当从/nest路由时,它们会产生AComponent和BComponent。为了演示,这些组件非常简单。 <li routerLink="/">...</li>可让您导航出巢路线以通过导航到主路线来重置示例。

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

 @Component({
  selector: 'app-a',
  template: `
  <p>a works!</p>
  `
 })
 export class AComponent { }
import { Component } from '@angular/core';

 @Component({
  selector: 'app-b',
  template: `
  <p>b works!</p>
  `
 })
 export class BComponent { }

children: []数组接受Route对象作为元素。 children: []也可以适用于任何这些元素。这些元素的孩子可以继续筑巢。然而,这种模式可以继续用于许多层嵌套。为每个嵌套路由层插入router-outlet到模板中。

无论Route对象的嵌套级别如何,路由技术都适用。参数化技术仅在一个方面不同。子路径只能通过ActivatedRoute.parent.params访问其父级参数。 ActivatedRoute.params针对相同级别的嵌套路由。这不包括父级路由及其参数。

Route保护特别适合嵌套路由。一个Route对象可以限制对其所有嵌套(子)路由的访问。

守卫路线

Web应用程序通常由公共和私有数据组成。两种类型的数据往往都有自己的带有_保护_路由的页面。这些路由允许/限制访问,具体取决于用户的权限。未经授权的用户可能会与受保护的路由进行交互。如果用户尝试访问其路由内容,则该路由应阻止该用户。

Angular提供了一组可以附加到任何路由的身份验证保护。这些方法根据用户与受保护路由的交互方式自动触发。

  • canActivate(...) - 当用户尝试访问路由时触发

  • canActivateChild(...) - 当用户尝试访问路由的嵌套(子)路由时触发

  • canDeactivate(...) - 当用户尝试离开路线时触发

Angular的防护方法可从@angular/router 。为了帮助他们进行身份验证,他们可以选择接收一些参数。这些参数不通过依赖注入注入。在引擎盖下,每个值都作为参数传递给调用的guard方法。

  • ActivatedRouteSnapshot - 适用于所有三个

  • RouterStateSnapshot - 可用于所有三个

  • Component - 可用于canDeactivate(...)

ActivatedRouteSnapshot提供对受保护路由的路由参数的访问。 RouterStateSnapshot公开与路由匹配的URL(统一资源定位符)Web地址。 Component引用路由呈现的组件。

为了保护路由,实现保护方法的类首先需要作为服务存在。该服务可以注入AppRoutingModule以保护其Routes 。服务的标记值可以注入任何一个Route对象。

import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';

 import { AuthService } from '../../services/auth.service';
 import { UserService } from '../../services/user.service';

 import { PrivateNestComponent } from '../../components/private-nest/private-nest.component';
 import { PrivateAComponent } from '../../components/private-nest/private-a/private-a.component';
 import { PrivateBComponent } from '../../components/private-nest/private-b/private-b.component';

 const routes: Routes = [
  {
    path: 'private-nest',
    component: PrivateNestComponent,
    canActivate: [ AuthService ], // !!!
    canActivateChild: [ AuthService ], // !!!
    canDeactivate: [ AuthService ], // !!!
    children: [
      { path: 'private-A', component: PrivateAComponent },
      { path: 'private-B', component: PrivateBComponent }
    ]
  }
 ];

 @NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ],
  providers: [
    AuthService,
    UserService
  ]
 })
 export class AppRoutingModule { }

canActivatecanActivateChildcanDeactivate来自AuthService。服务实现将在UserService实现的同时显示。

UserService提供验证用户身份所需的信息。 AuthService防护方法实现执行身份验证。 AppRoutingModule必须将两个服务包含在其providers数组中。这样模块的注入器就知道如何实例化它们。

嵌套路由存在于/private-nest路径之外。 /private-nestRoute对象包含一些新字段。他们的名字应该看起来很熟悉,因为他们反映了相应的防守方

触发时,每个字段在服务内部触发其同名的方法实现。任何数量的服务也可以填充此数组。每个服务的方法实现都经过测试。它们必须返回一个布尔值或一个发出布尔值的Observable

请参阅下面的AuthService和UserService实现。

// user.service.ts

 import { Injectable } from '@angular/core';
 import { Router } from '@angular/router';

 class TheUser {
  constructor(public isLoggedIn: boolean = false) { }

  toggleLogin() {
    this.isLoggedIn = true;
  }

  toggleLogout() {
    this.isLoggedIn = false;
  }
 }

 const globalUser = new TheUser();

 @Injectable({
  providedIn: 'root'
 })
 export class UserService {
  theUser: TheUser = globalUser;

  constructor(private router: Router) { }

  get isLoggedIn() {
    return this.theUser.isLoggedIn;
  }

  login() {
    this.theUser.toggleLogin();
  }

  logout() {
    this.theUser.toggleLogout();
    this.router.navigate(['/']);
  }
 }

随着UserService的每个实例化传递相同的TheUser实例。 TheUser可以访问isLoggedIn来确定用户的登录状态。另外两个公共方法让UserService切换isLoggedIn的值。这样用户就可以登录和注销。

您可以将TheUser视为全局实例。 UserService是一个可实例化的接口,用于配置此全局。从一个UserService实例化对TheUser更改适用于每个其他UserService实例。 UserService实现到AuthService以提供对TheUser的isLoggedIn的访问以TheUser身份验证。

import { Component, Injectable } from '@angular/core';
 import { CanActivate, CanActivateChild, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

 import { UserService } from './user.service';

 @Injectable({
  providedIn: 'root'
 })
 export class AuthService implements CanActivate, CanActivateChild, CanDeactivate<Component> {
  constructor(private user: UserService) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    if (this.user.isLoggedIn)
      return true;
    else
      return false;
  }

  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.canActivate(route, state);
  }

  canDeactivate(component: Component, route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    if (!this.user.isLoggedIn || window.confirm('Leave the nest?'))
      return true;
    else
      return false;
  }
 }

AuthService实现从@angular/router导入的每个保护方法。每个保护方法都映射到PrivateNestComponent的Route对象中的相应字段。 UserService的实例从AuthService构造函数实例化。 AuthService确定用户是否可以继续使用UserService公开的isLoggedIn

从警卫返回false指示路由阻止用户路由。返回值为true允许用户前往其路由目的地。如果多个服务进行身份验证,则它们都必须返回true以允许访问。 canActivateChild保护canActivateChild的子路由。该保护方法考虑用户通过地址栏绕过PrivateNestComponent。

Guard方法参数在调用时自动传入。虽然该示例没有使用它们,但它们确实从路线提供了有用的信息。开发人员可以使用此信息来帮助验证用户身份。

AppComponent还实例化UserService以便在其模板中直接使用。 AppComponent和AuthService的UserService实例化引用相同的用户类( TheUser )。

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

 import { UserService } from './services/user.service';

 @Component({
  selector: 'app-root',
  template: `
  <ul>
    <li routerLink="/private-nest">Enter the secret nest!</li>
    <li routerLink="/">Leave the secret nest!</li>
    <li *ngIf="user.isLoggedIn"><button (click)="user.logout()">LOGOUT</button></li>
    <li *ngIf="!user.isLoggedIn"><button (click)="user.login()">LOGIN</button></li>
  </ul>
  <router-outlet></router-outlet>
  `
 })
 export class AppComponent {
  constructor(private user: UserService) { }
 }

UserService处理AppComponent的所有逻辑。 AppComponent主要关注其模板。 UserService确实从类构造函数实例化为useruser数据确定模板的功能。

结论

路由在应用程序的组织和限制部分之间达到了很好的平衡。较小的应用程序(如博客或致敬页面)可能不需要任何路由。即使这样,包括一点哈希路由也不会受到伤害。用户可能只想参考页面的一部分。

Angular应用自己的构建于HTML5 历史API之上的路由库。此API省略了散列路由,而是使用pushState(...)replaceState(...)方法。他们更改网址URL而不刷新页面。 Angular中的默认路径位置路由策略以这种方式工作。如果RouterModule.forRoot(routes, { useHash: true })设置RouterModule.forRoot(routes, { useHash: true })启用散列路由。

本文重点介绍默认路径定位策略。无论策略如何,许多路由实用程序都可用于路由应用程序。 RouterModule通过其导出公开这些实用程序。使用RouterModule可以实现基本,参数化,嵌套和保护路由。有关包含延迟加载路由的更高级实现,请参阅以下链接。

更多Angular教程

学习更多Angular教程