Angular 4.x 路由快速入门学习


Posted in Javascript onMay 03, 2017

路由是 Angular 应用程序的核心,它加载与所请求路由相关联的组件,以及获取特定路由的相关数据。这允许我们通过控制不同的路由,获取不同的数据,从而渲染不同的页面。

接下来我们将按照以下目录的内容,介绍 Angular 的路由。具体目录如下:

目录

  1. Installing the router
    1. Base href
  2. Using the router
    1. RouterModule.forRoot
    2. RouterModule.forChild
  3. Configuring a route
  4. Displaying routes
  5. Futher configuration
    1. Dynamic routes
    2. Child routes
    3. Component-less routes
    4. loadChildren
  6. Router directives
    1. routerLink
    2. routerLinkActive
  7. Router API

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="/" rel="external nofollow" >
 <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: './settings/settings.module#SettingsModule'
 }
];

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

需要注意的是,我们没有将 SettingsModule 导入到我们的 AppModule 中,而是通过 loadChildren 属性,告诉 Angular 路由依据 loadChildren 属性配置的路径去加载 SettingsModule 模块。这就是模块懒加载功能的具体应用,当用户访问 /settings/** 路径的时候,才会加载对应的 SettingsModule 模块,这减少了应用启动时加载资源的大小。

另外我们传递一个字符串作为 loadChildren 的属性值,该字符串由三部分组成:

  1. 需要导入模块的相对路径
  2. # 分隔符
  3. 导出模块类的名称

了解完路由的一些高级选项和功能,接下来我们来介绍路由指令。

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 官文文档。

我有话说

除了使用 navigate() 方法外还有没有其它方法可以实现页面导航?

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)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript 表单规则集合对象
Jul 21 Javascript
JavaScript 以对象为索引的关联数组
May 19 Javascript
基于jquery的弹出提示框始终处于窗口的居中位置(类似于alert弹出框的效果)
Sep 28 Javascript
JS定时关闭窗口的实例
May 22 Javascript
触屏中的JavaScript事件分析
Feb 06 Javascript
jQuery 检查某个元素在页面上是否存在实例代码
Oct 27 Javascript
vue源码学习之Object.defineProperty 对数组监听
May 30 Javascript
vue-router 实现导航守卫(路由卫士)的实例代码
Sep 02 Javascript
微信小程序--获取用户地理位置名称(无须用户授权)的方法
Apr 29 Javascript
解决 viewer.js 动态更新图片导致无法预览的问题
May 14 Javascript
微信小程序wx.getUserInfo授权获取用户信息(头像、昵称)的实现
Aug 19 Javascript
详解CocosCreator消息分发机制
Apr 16 Javascript
javaScript 逻辑运算符使用技巧整理
May 03 #Javascript
浅谈Node.js轻量级Web框架Express4.x使用指南
May 03 #Javascript
vue的Virtual Dom实现snabbdom解密
May 03 #Javascript
JavaScript中undefined和null的区别
May 03 #Javascript
Node.js对MongoDB数据库实现模糊查询的方法
May 03 #Javascript
xmlplus组件设计系列之文本框(TextBox)(3)
May 03 #Javascript
利用Mongoose让JSON数据直接插入或更新到MongoDB
May 03 #Javascript
You might like
php中使用explode查找某个字符是否存在的方法
2011/07/12 PHP
PHP获取MAC地址的函数代码
2011/09/11 PHP
PHP简单实现图片格式转换(jpg转png,gif转png等)
2019/10/30 PHP
个人总结的一些关于String、Function、Array的属性和用法
2007/01/10 Javascript
CLASS_CONFUSION JS混淆 全源码
2007/12/12 Javascript
Javascript 遍历对象中的子对象
2009/07/03 Javascript
js文件中调用js的实现方法小结
2009/10/23 Javascript
Microsoft Ajax Minifier 压缩javascript的方法
2010/03/05 Javascript
20条学习javascript的编程规范的建议
2014/11/28 Javascript
jQuery动态效果显示人物结构关系图的方法
2015/05/07 Javascript
快速掌握Node.js事件驱动模型
2016/03/21 Javascript
详解vue-router和vue-cli以及组件之间的传值
2017/07/04 Javascript
详解Vue-cli中的静态资源管理(src/assets和static/的区别)
2018/06/19 Javascript
python调用java的Webservice示例
2014/03/10 Python
Python 26进制计算实现方法
2015/05/28 Python
使用Python多线程爬虫爬取电影天堂资源
2016/09/23 Python
Python中对象的引用与复制代码示例
2017/12/04 Python
Python编程图形库之Pillow使用方法讲解
2018/12/28 Python
Django之提交表单与前后端交互的方法
2019/07/19 Python
TensorFlow内存管理bfc算法实例
2020/02/03 Python
基于PyTorch的permute和reshape/view的区别介绍
2020/06/18 Python
实例讲解Python 迭代器与生成器
2020/07/08 Python
Python Tricks 使用 pywinrm 远程控制 Windows 主机的方法
2020/07/21 Python
viagogo英国票务平台:演唱会、体育比赛、戏剧门票
2017/03/24 全球购物
美国医疗用品、医疗设备和家庭保健用品商店:Medical Supply Depot
2018/07/08 全球购物
计算机专业个人求职自荐信
2013/09/21 职场文书
行政人员工作职责
2013/12/05 职场文书
基层党员学习党的群众路线教育实践活动心得体会
2014/11/04 职场文书
工程部经理岗位职责
2015/02/02 职场文书
关于倡议书的范文
2015/04/29 职场文书
2015年世界环境日活动方案
2015/05/05 职场文书
七一慰问简报
2015/07/20 职场文书
2019企业文化管理制度范本!
2019/08/06 职场文书
pytorch 如何使用float64训练
2021/05/24 Python
SQL实现LeetCode(196.删除重复邮箱)
2021/08/07 MySQL
使用Mysql计算地址的经纬度距离和实时位置信息
2022/04/29 MySQL