淺談Angular中navigate()和navigateByUrl()使用方法的區別

2021-06-25 13:00:14
路由是應用程式的核心,本篇文章帶大家瞭解一下Angular Router,詳細介紹一下navigate()和navigateByUrl()用法的區別。

路由是 Angular 應用程式的核心,它載入與所請求路由相關聯的元件,以及獲取特定路由的相關資料。這允許我們通過控制不同的路由,獲取不同的資料,從而渲染不同的頁面。【相關教學推薦:《》】

Installing the router

首先第一件事,我們需要安裝 Angular Router。你可以通過執行以下任一操作來執行此操作:

yarn add @angular/router
# OR
npm i --save @angular/router

以上命令執行後,將會自動下載 @angular/router 模組到 node_modules 資料夾中。

Base href

我們需要做的最後一件事,是將 <base> 標籤新增到我們的 index.html 檔案中。路由需要根據這個來確定應用程式的根目錄。例如,當我們轉到 http://example.com/page1 時,如果我們沒有定義應用程式的基礎路徑,路由將無法知道我們的應用的託管地址是 http://example.com 還是 http://example.com/page1

這件事操作起來很簡單,只需開啟專案中的 index.html 檔案,新增相應的 <base> 標籤,具體如下:

<!doctype html>
<html>
  <head>
    <base href="/">
    <title>Application</title>
  </head>
  <body>
    <app-root></app-root>
  </body>
</html>

以上設定資訊告訴 Angular 路由,應用程式的根目錄是 /

Using the router

要使用路由,我們需要在 AppModule 模組中,匯入 RouterModule。具體如下:

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

import { AppComponent } from './app.component';

@NgModule({
  imports: [
    BrowserModule,
    RouterModule
  ],
  bootstrap: [
    AppComponent
  ],
  declarations: [
    AppComponent
  ]
})
export class AppModule {}

此時我們的路由還不能正常工作,因為我們還未設定應用程式路由的相關資訊。RouterModule 物件為我們提供了兩個靜態的方法:forRoot()forChild() 來設定路由資訊。

RouterModule.forRoot()

RouterModule.forRoot() 方法用於在主模組中定義主要的路由資訊,通過呼叫該方法使得我們的主模組可以存取路由模組中定義的所有指令。接下來我們來看一下如何使用 forRoot()

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

export const ROUTES: Routes = [];

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(ROUTES)
  ],
  // ...
})
export class AppModule {}

我們通過使用 const 定義路由的設定資訊,然後把它作為引數呼叫 RouterModule.forRoot() 方法,而不是直接使用 RouterModule.forRoot([...]) 這種方式,這樣做的好處是方便我們在需要的時候匯出 ROUTES 到其它模組中。

RouterModule.forChild()

RouterModule.forChild()Router.forRoot() 方法類似,但它只能應用在特性模組中。
友情提示:根模組中使用 forRoot(),子模組中使用 forChild()
這個功能非常強大,因為我們不必在一個地方(我們的主模組)定義所有路由資訊。反之,我們可以在特性模組中定義模組特有的路由資訊,並在必要的時候將它們匯入我們主模組。RouterModule.forChild() 的使用方法如下:

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

export const ROUTES: Routes = [];

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild(ROUTES)
  ],
  // ...
})
export class ChildModule {}

通過以上範例,我們知道在主模組和特性模組中,路由設定物件的型別是一樣的,區別只是主模組和特性模組中需呼叫不同的方法,來設定模組路由。接下來我們來介紹一下如何設定 ROUTES 物件。

Configuring a route

我們定義的所有路由都是作為 ROUTES 陣列中的物件。首先,為我們的主頁定義一個路由:

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

import { HomeComponent } from './home/home.component';

export const ROUTES: Routes = [
  { path: '', component: HomeComponent }
];

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(ROUTES)
  ],
  // ...
})
export class AppModule {}

範例中我們通過 path 屬性定義路由的匹配路徑,而 component 屬性用於定義路由匹配時需要載入的元件。

友情提示:我們使用 path: '' 來匹配空的路徑,例如:https://yourdomain.com

Displaying routes

設定完路由資訊後,下一步是使用一個名為 router-outlet 的指令告訴 Angular 在哪裡載入元件。當 Angular 路由匹配到響應路徑,併成功找到需要載入的元件時,它將動態建立對應的元件,並將其作為兄弟元素,插入到 router-outlet 元素中。

在我們 AppComponent 元件中,我們可以在任意位置插入 router-outlet指令:

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

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <h3>Our app</h3>
      <router-outlet></router-outlet>
    </div>
  `
})
export class AppComponent {}

我們現在已經建立了應用程式的主路由,我們可以進一步瞭解路由的其它設定選項。

Further configuration

到目前為止我們已經介紹的內容只是一個開始 ,接下來我們來看看其它一些選項和功能。

Dynamic routes

如果路由始終是靜態的,那沒有多大的用處。例如 path: '' 是載入我們 HomeComponent 元件的靜態路由。我們將介紹動態路由,基於動態路由我們可以根據不同的路由引數,渲染不同的頁面。

例如,如果我們想要在個人資料頁面根據不同的使用者名稱顯示不同的使用者資訊,我們可以使用以下方式定義路由:

import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';

export const ROUTES: Routes = [
  { path: '', component: HomeComponent },
  { path: '/profile/:username', component: ProfileComponent }
];

這裡的關鍵點是 : ,它告訴 Angular 路由,:username 是路由引數,而不是 URL 中實際的部分。

友情提示:如果沒有使用 : ,它將作為靜態路由,僅匹配 /profile/username 路徑
現在我們已經建立一個動態路由,此時最重要的事情就是如何獲取路由引數。要存取當前路由的相關資訊,我們需要先從 @angular/router 模組中匯入 ActivatedRoute ,然後在元件類別建構函式中注入該物件,最後通過訂閱該物件的 params 屬性,來獲取路由引數,具體範例如下:

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

@Component({
  selector: 'profile-page',
  template: `
    <div class="profile">
      <h3>{{ username }}</h3>
    </div>
  `
})
export class SettingsComponent implements OnInit {
  username: string;
  constructor(private route: ActivatedRoute) {}
  ngOnInit() {
    this.route.params.subscribe((params) => this.username = params.username);
  }
}

介紹完動態路由,我們來探討一下如何建立 child routes。

Child routes

實際上每個路由都支援子路由,假設在我們 /settings 設定頁面下有 /settings/profile/settings/password 兩個頁面,分別表示個人資料頁和修改密碼頁。

我們可能希望我們的 /settings 頁面擁有自己的元件,然後在設定頁面元件中顯示 /settings/profile/settings/password 頁面。我們可以這樣做:

import { SettingsComponent } from './settings/settings.component';
import { ProfileSettingsComponent } from './settings/profile/profile.component';
import { PasswordSettingsComponent } from './settings/password/password.component';

export const ROUTES: Routes = [
  { 
    path: 'settings', 
    component: SettingsComponent,
    children: [
      { path: 'profile', component: ProfileSettingsComponent },
      { path: 'password', component: PasswordSettingsComponent }
    ]
  }
];

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(ROUTES)
  ],
})
export class AppModule {}

在這裡,我們在 setttings 路由中定義了兩個子路由,它們將繼承父路由的路徑,因此修改密碼頁面的路由匹配地址是 /settings/password ,依此類推。

接下來,我們需要做的最後一件事是在我們的 SettingsComponent 元件中新增 router-outlet 指令,因為我們要在設定頁面中呈現子路由。如果我們沒有在 SettingsComponent 元件中新增 router-outlet 指令,儘管 /settings/password 匹配修改密碼頁面的路由地址,但修改密碼頁面將無法正常顯示。具體程式碼如下:

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

@Component({
  selector: 'settings-page',
  template: `
    <div class="settings">
      <settings-header></settings-header>
      <settings-sidebar></settings-sidebar>
      <router-outlet></router-outlet>
    </div>
  `
})
export class SettingsComponent {}

Component-less routes

另一個很有用的路由功能是 component-less 路由。使用 component-less 路由允許我們將路由組合在一起,並讓它們共用路由設定資訊和 outlet。

例如,我們可以定義 setttings 路由而不需要使用 SettingsComponent 元件:

import { ProfileSettingsComponent } from './settings/profile/profile.component';
import { PasswordSettingsComponent } from './settings/password/password.component';

export const ROUTES: Routes = [
  {
    path: 'settings',
    children: [
      { path: 'profile', component: ProfileSettingsComponent },
      { path: 'password', component: PasswordSettingsComponent }
    ]
  }
];

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(ROUTES)
  ],
})
export class AppModule {}

此時, /settings/profile/settings/password 路由定義的內容,將顯示在 AppComponent 元件的 router-outlet 元素中。

loadChildren

我們也可以告訴路由從另一個模組中獲取子路由。這將我們談論的兩個想法聯絡在一起 - 我們可以指定另一個模組中定義的子路由,以及通過將這些子路由設定到特定的路徑下,來充分利用 component-less 路由的功能。

讓我們建立一個 SettingsModule 模組,用來儲存所有 setttings 相關的路由資訊:

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

export const ROUTES: Routes = [
  {
    path: '',
    component: SettingsComponent,
    children: [
      { path: 'profile', component: ProfileSettingsComponent },
      { path: 'password', component: PasswordSettingsComponent }
    ]
  }
];

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild(ROUTES)
  ],
})
export class SettingsModule {}

需要注意的是,在 SettingsModule 模組中我們使用 forChild() 方法,因為 SettingsModule 不是我們應用的主模組。

另一個主要的區別是我們將 SettingsModule 模組的主路徑設定為空路徑 (’’)。因為如果我們路徑設定為 /settings ,它將匹配 /settings/settings ,很明顯這不是我們想要的結果。通過指定一個空的路徑,它就會匹配 /settings 路徑,這就是我們想要的結果。

那麼 /settings 路由資訊,需要在哪裡設定?答案是在 AppModule 中。這時我們就需要用到 loadChildren 屬性,具體如下:

export const ROUTES: Routes = [
  {
    path: 'settings',
    loadChildren: () => import('./settings/settings.module').then(it => it.SettingsModule)
  }
];

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(ROUTES)
  ],
  // ...
})
export class AppModule {}

需要注意的是,我們沒有將 SettingsModule 匯入到我們的 AppModule 中,而是通過 loadChildren 屬性,告訴 Angular 路由依據 loadChildren 屬性設定的路徑去載入 SettingsModule 模組。這就是模組懶載入功能的具體應用,當使用者存取 /settings/** 路徑的時候,才會載入對應的 SettingsModule 模組,這減少了應用啟動時載入資源的大小。

另外我們傳遞一個字串作為 loadChildren 的屬性值,該字串由三部分組成:

需要匯入模組的相對路徑
# 分隔符
匯出模組類的名稱

瞭解完路由的一些高階選項和功能,接下來我們來介紹路由指令。

Router Directives

除了 router-outlet 指令,路由模組中還提供了一些其它指令。讓我們來看看它們如何與我們之前介紹的內容結合使用。

routerLink

為了讓我們連結到已設定的路由,我們需要使用 routerLink 指令,具體範例如下:

<nav>
  <a routerLink="/">Home</a>
  <a routerLink="/settings/password">Change password</a>
  <a routerLink="/settings/profile">Profile Settings</a>
</nav>

當我們點選以上的任意連結時,頁面不會被重新載入。反之,我們的路徑將在 URL 位址列中顯示,隨後進行後續檢視更新,以匹配 routerLink 中設定的值。

友情提示:我們也可以將 routerLink 的屬性值,改成陣列形式,以便我們傳遞特定的路由資訊
如果我們想要連結到動態的路由地址,且該地址有一個 username 的路由變數,則我們可以按照以下方式設定 routerLink 對應的屬性值:

<a [routerLink]="['/profile', username]">
  Go to {{ username }}'s profile.
</a>

routerLinkActive

在實際開發中,我們需要讓使用者知道哪個路由處於啟用狀態,通常情況下我們通過向啟用的連結新增一個 class 來實現該功能。為了解決上述問題,Angular 路由模組為我們提供了 routerLinkActive 指令,該指令的使用範例如下:

<nav>
  <a routerLink="/settings" routerLinkActive="active">Home</a>
  <a routerLink="/settings/password" routerLinkActive="active">Change password</a>
  <a routerLink="/settings/profile" routerLinkActive="active">Profile Settings</a>
</nav>

通過使用 routerLinkActive 指令,當 a 元素對應的路由處於啟用狀態時,active 類將會自動新增到 a 元素上。
最後,我們來簡單介紹一下 Router API。

Router API

我們可以通過路由還提供的 API 實現與 routerLink 相同的功能。要使用 Router API,我們需要在元件類中注入 Router 物件,具體如下:

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

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <h3>Our app</h3>
      <router-outlet></router-outlet>
    </div>
  `
})
export class AppComponent {
  constructor(private router: Router) {}
}

元件類中注入的 router 物件中有一個 navigate() 方法,該方法支援的引數型別與 routerLink 指令一樣,當呼叫該方法後,頁面將會自動跳轉到對應的路由地址。具體使用範例如下:

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

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <h3>Our app</h3>
      <router-outlet></router-outlet>
    </div>
  `
})
export class AppComponent implements OnInit {
  constructor(private router: Router) {}
  ngOnInit() {
    setTimeout(() => {
      this.router.navigate(['/settings']);
    }, 5000);
  }
}

若以上程式碼成功執行,使用者介面將在 5 秒後被重定向到 /settings 頁面。這個方法非常有用,例如當檢測到使用者尚未登入時,自動重定向到登入頁面。

另一個使用範例是演示頁面跳轉時如何傳遞資料,具體如下:

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

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <h3>Users</h3>
      <div *ngFor="let user of users">
        <user-component 
          [user]="user"
          (select)="handleSelect($event)">
        </user-component>
      </div>
      <router-outlet></router-outlet>
    </div>
  `
})
export class AppComponent implements OnInit {
  users: Username[] = [
    { name: 'toddmotto', id: 0 },
    { name: 'travisbarker', id: 1 },
    { name: 'tomdelonge', id: 2 }
  ];
  
  constructor(private router: Router) {}
  
  handleSelect(event) {
    this.router.navigate(['/profile', event.name]);
  }
}

Angular 路由的功能非常強大,既可以使用指令方式也可以使用命令式 API,希望本文可以幫助你儘快入門,若要進一步瞭解路由詳細資訊,請存取 - Angular Router 官文檔案。

路由傳參範例
/router/823712312938123;h=h;c=c?code=code

  • HTML:
<a [routerLink]="['/router', '823712312938123', {h:'h',c:'c'}]"
     [queryParams]="{code:'code'}">RouterLink 跳轉</a>
  <a (click)="goToDetail()">Navigate 跳轉</a>
  • TS:
 this.router.navigate([`router/823712312938123`, {h: 'h', c: 'c'}],
      {queryParams: {code: 'code'}})

在component中取資料
router-demo.component.ts

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

@Component({
  selector: 'app-router-demo',
  templateUrl: './router-demo.component.html',
  styleUrls: ['./router-demo.component.less']
})
export class RouterDemoComponent implements OnInit {

  constructor(private activatedRoute: ActivatedRoute) {
  }

  ngOnInit(): void {
    const {params, queryParams} = this.activatedRoute.snapshot
    console.log('params', params)
    console.log('queryParams', queryParams, queryParams)

  }

}

router取資料

Angular Router API 提供了 navigate()navigateByUrl() 兩個方法來實現頁面導航。兩者區別如下:

  • 使用router.navigateByUrl() 方法與直接改變位址列上的 URL 地址一樣,我們使用了一個新的 URL 地址。
  • 然而 router.navigate() 方法基於一系列輸入引數,產生一個新的 URL 地址。

為了更好理解兩者區別,有例子,假設當前的 URL 地址是:

/inbox/11/message/22(popup:compose)

當呼叫 router.navigateByUrl('/inbox/33/message/44') 方法後,當前的 URL 地址將變成/inbox/33/message/44

但若是呼叫 router.navigate('/inbox/33/message/44') 方法,當前的 URL 地址將變成 /inbox/33/message/44(popup:compose)

更多程式設計相關知識,請存取:!!

以上就是淺談Angular中navigate()和navigateByUrl()使用方法的區別的詳細內容,更多請關注TW511.COM其它相關文章!