7.《Angular路由》

一、SPA

单页Web应用(single page web application,SPA),就是只有一张Web页面的应用。单页应用程序 (SPA) 是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。 [1] 浏览器一开始会加载必需的HTML、CSS和JavaScript,所有的操作都在这张页面上完成,都由JavaScript来控制。因此,对单页应用来说模块化的开发和设计显得相当重要。

特点

  • 速度:更好的用户体验,让用户在web app感受native app的速度和流畅,

  • MVC:经典MVC开发模式,前后端各负其责。

  • ajax:重前端,业务逻辑全部在本地操作,数据都需要通过AJAX同步、提交。

  • 路由:在URL中采用#号来作为当前视图的地址,改变#号后的参数,页面并不会重载。

单页Web应用(single page web application,SPA)是当今网站开发技术的弄潮儿,很多传统网站都在或者已经转型为单页Web应用,新的单页Web应用网站(包括移动平台上的)也如雨后春笋般涌现在人们的面前,如Gmail、Evernote、Trello等。如果你是一名Web开发人员,却还没开发过或者甚至是没有听说过单页应用,那你已经Out很久了。

单页Web应用和前端工程师们息息相关,因为主要的变革发生在浏览器端,用到的技术其实还是HTML+CSS+JavaScript,所有的浏览器都原生支持,当然有的浏览器因为具备一些高级特性,从而使得单页Web应用的用户体验更上一层楼。关于单页应用的优点和缺点,网上讲解的文章有很多,这里就不展开论述了。 单页Web应用,顾名思义,就是只有一张Web页面的应用。浏览器一开始会加载必需的HTML、CSS和JavaScript,之后所有的操作都在这张页面上完成,这一切都由JavaScript来控制。因此,单页Web应用会包含大量的JavaScript代码,复杂度可想而知,模块化开发和设计的重要性不言而喻。

二、路由基础

1.新建项目

ng new router --routing

2.基础知识

名称 简介
Routes 路由配置,保存着哪个URL对应展示哪个组件,以及在哪个RouterOutlet中展示组件。
RouterOutlet 在Html中标记路由内容呈现位置的占位符指令。
Router 负责在运行时执行路由的对象,可以通过调用其navigate()和navigateByUrl()方法来导航到一个指定的路由。
RouterLink 在Html中声明路由导航用的指令。
ActivedRoute 当前激活的路由对象,保存着当前路由的信息,如路由地址,路由参数等。

各参数在文件中的位置.png

3.路由配置

  • Routes
    新建home、product组件配置到路由中,注意path不要以/开头。


    路由配置.png
  • RouterOutlet


    router-outlet后显示路由内容.png
  • RouterLink
    以 / 开头表示路由到根路由


    使用routerLink.png
  • Router
    html文件中绑定事件


    html导航配置.png

    ts文件中使用navigate方法


    ts导航事件.png
  • 通配符路由

通配符路由的path是两个星号(**),它会匹配任何URL。当路由器匹配不上任何路由时会选择这个路由。

新建404组件

ng g component code404

app-routing.module中配置


配置.png

运行效果


404.png

注意:路由器使用先匹配者优先的策略来选择路由。通配符路由通常是路由配置中最后一个。

4.路由时传递数值

  • 在查询参数中传递数据
/product?id=1&name=2  =>  ActivatedRoute.queryParams[id]
单/多参数.png
目标组件ts.png
  • 在路由路径中传递数据
{path:' /product/:id'}  =>  /product/1  =>  ActivatedRoute.queryParams[id]
app-routing.module.png

app.component.html.png

product.component.ts.png
  • 在路由配置中传递数据
{path:' /product/:id',component:'ProductComponent',data:[{isProd:true}] 
=>  ActivatedRoute.data[0][isProd]

参数快照和参数订阅
修改toProductDetails方法

详情按钮.png

这时我们点击按钮
效果.png

看似没什么问题,这时我们再点击商品详情链接
error.png

问题出现了,从home组件路由到product组件时候,product组件被创建,这时它的的constructor方法被调用,ngOnInit()被调用一次,页面上的参数id会根据URL信息拿到正确的参数。当我们从product组件又调到product组件时,由于在product组件已经被创建过一次了,所以constructor()方法不会被调用,所以ngOnInit方法也不会再调用 ,所以id属性依然保持着第一次被创建时赋予的值。解决这个问题的方法就是参数订阅
参数订阅.png

//多参数路由传值写法
<a [routerLink]="['/product',1,33]">商品详情</a>
<a [routerLink]="['/product/1/33']">商品详情</a>
TS文件:nav() {
    this.router.navigate(['/product', 2, 666]);
}
路由:{path: 'product/:id/:name', component: ProductComponent},
或
<a [routerLink]="['/product']" [queryParams]="{id:1,name:33}">商品详情</a>
nav() {
   this.router.navigate(['/product'], {queryParams: {id: 19, name: 33}});
}

5.重定向路由
在用户访问一个特定的网址时,将其重定向到另一个指定的地址。

www.aaa.com =>  www.aaa.com/products
www.aaa.com/x =>  www.aaa.com/y

重定向路由.png

6.子路由

{path: 'home', component: HomeComponent}
配置子路由
{path: 'home', component: HomeComponent,children:[
  {path: '', component: XXXXComponent},
  {path: '/YYYY', component: YYYYComponent}
]}

新建两个组件

ng g component sellerInfo
ng g component productDesc
app-routing.module.png
product.component.html.png

seller-info.ts.png

注意:
子路由可以无限嵌套;
路由信息都是模块module中配置的,组件本身并不知道路由相关的信息。

7.辅助路由

1.页面插座,辅助路由的写法是带有name属性
<router-outlet></router-outlet>
<router-outlet name="msg"></router-outlet>

2.路径配置,名为msg的辅助路由可以用来显示XXX组件和YYY组件
{ path:'XXX',XXXcomponent,outlet:'msg' }
{ path:'YYY',YYYcomponent,outlet:'msg' }

3.路由参数跳转路由,primary控制主路由,不管在哪个页面,点击后主路由都会显示home组件
<a [routerLink]="[{outlets:{primary:'home',msg:'XXX'}}]">开始咨询</a>
主路由-辅助路由。路由的配置:名为msg的辅助路由可以显示xxx和yyy组件。点击Xxx时,主路由显示home组件,辅助路由显示XXX组件

demo示例
在app组件上再定义一个插座来显示聊天面板。


app.component.htm.png

单独开发一个聊天组件,只显示在新定义的插座上。

ng g component chat

通过路由参数控制新插座是否显示聊天面板。


app-routing.module.png

另一种方式


使用router跳转的写法.png

8.路由守卫

也就是,拦截器,在进入或离开路由前执行一些逻辑

应用场景

  • 只有当用户已经登陆并拥有某项权限时才进入某路由
  • 一个由多个表单组件组成的向导,例如注册流程,用户只有在当前组件填写了满足要求的信息才进入下一个路由。
  • 当用户未执行保存操作而试图离开当前导航时提醒用户。

(1)CanActivate : 处理导航到某路由之前的情况,如:付费用户

一个用来定义类的接口,路由器会首先调用它来决定是否应该激活该组件。应该返回布尔值或能解析为布尔值的可观察对象(Observable)或承诺(Promise)。

canActivate demo


login.guard.ts.png

app-routing.module.png
canActivate demo2

//1.编写CanActivate守卫PermissionGuard
// 在guard/permission.guard.ts中

//导入CanActivate守卫
import { CanActivate } from '@angular/router';

export class PermissionGuard implements CanActivate {
    canActivate(){
        var hasPermission:boolean = Math.random() > 0.5;
        if (!hasPermission) {
            console.log('用户无权限访问');
        };
        return hasPermission;
        //返回值true时进入路由
    }
}

//2.在路由配置中增加canActivate属性
// 在app-routing.module.ts中

//导入PermissionGuard
import { PermissionGuard } from './guard/permission.guard';

const routes: Routes = [{path:'stocks/:id',component:StocksComponent,canActivate:[PermissionGuard]}]

//3.写入providers
// 在app.module.ts中
//先导入
import { PermissionGuard } from './guard/permission.guard';

providers: [PermissionGuard]

(2)CanDeactivate : 处理从当前路由离开的情况,如:是否未保存就离开

一个用来定义类的接口,路由器在导航后会首先调用它来决定是否应该取消该组件的激活状态。应该返回布尔值或能解析为布尔值的可观察对象(Observable)或承诺(Promise)。

返回布尔值为true时离开路由。


unsave.guard.ts.png
app-routing.module.png
CanDeactivate demo2

//1.编写CanDeactivate守卫focusGuard
// 在guard/permission.guard.ts中

//导入CanDeactivate守卫和要离开的组件
import { CanDeactivate } from '@angular/router';
import { StocksComponent } from '../stocks/stocks.component';

//判断component上isfocus是否已关注,未关注就弹出窗口 
export class focusGuard implements CanDeactivate<StocksComponent> {
    canDeactivate(component:StocksComponent){
        if(!component.isfocus){
            return window.confirm('不关注吗')
        }else{
            return true;
        }
    }
}


//2.在stocks.component.html (要守卫的组件)上写按钮
<button (click)="focus()">关注</button>

//3.在stocks.component.ts 写逻辑
  focus(){
    this.isfocus = true;
  }

//4.在路由配置中增加canDeactivate属性
// 在app-routing.module.ts中

//5.导入focusGuard
import { focusGuard } from './guard/focus.guard';

const routes: Routes = [{path:'stocks/:id',component:StocksComponent,canActivate:[PermissionGuard],canDeactivate:[focusGuard]}]

//6.写入providers
// 在app.module.ts中

//先导入
import { focusGuard } from './guard/focus.guard';

providers: [PermissionGuard,focusGuard]

(3)Resolve : 在路由激活之前获取路由数据,提高用户体验

一个用来定义类的接口,路由器会在渲染该路由之前先调用它来解析路由数据。应该返回一个值或能解析为值的可观察对象(Observable)或承诺(Promise)。

product.resolve.ts.png

product.component.ts.png

app-routing.module.png
Resolve demo2

在进入路由{path:”stock/:id”}时,当id=1时,实例化stock类

//1.在stockComponent中编写stock类的构造函数
export class StocksComponent implements OnInit {

  private stock:Stock;

  constructor() { }

  ngOnInit() {
  }
}
export class Stock{
  constructor(public id:number,public name:string){}
}
//2.编写Resolve:stockResolve,返回值将传入对应path的Resolve属性中,当id=1时,实例化stock并返回

//注意导入的内容
import { Resolve,ActivatedRouteSnapshot,RouterStateSnapshot,Router } from '@angular/router';
import { Stock } from '../stocks/stocks.component';
import { Observable } from 'rxjs';
import { Injectable } from '@angular/core';

//@Injectable() 使constructor中的依赖注入生效,组件有@Component注解所以不用写@Injectable()

@Injectable()
export class stockResolve implements Resolve<Stock>{

    constructor(private router:Router){}

    resolve(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable<Stock>|Promise<Stock>|Stock {
        let id = route.params['id'];
        //路由路径id为1时
        if(id == 1){
            console.log('这里是1');
            return new Stock(1,'IBM'); // 返回实例化值 stock
        }else{
            this.router.navigate(['/home']);
            return undefined;
        }
    }
}
//3.在路由配置中,path路径中增加Resolve属性:
{path:'stocks/:id',component:StocksComponent,canActivate:[PermissionGuard],canDeactivate:[focusGuard],resolve:{stock:stockResolve}
    //resolve对象格式,各种数据。stockResolve守卫传值给stock
}

//4.在stockComponent中的ngOnInit,data数据值来源于path中的stock,stock是Stock类型
ngOnInit() {
    this.routerInfo.data.subscribe((data:{stock:Stock})=>{
      this.stock = data.stock
      console.log(this.stock);
    });
  }
//5.在provider中写入stockResolve 
//先导入
import { stockResolve } from './guard/stock.resolve';

providers: [PermissionGuard,focusGuard,stockResolve ]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,667评论 18 139
  • 一:路由基础 什么是路由: 在web开发中,路由的概念由来已久,简而言之,就是利用URL的唯一性来指定特定的事物,...
    真的稻城阅读 6,020评论 2 7
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,182评论 25 707
  • 我们idea介入的行业是工业制造,工业互连网,我们发现目前的工业制造在很多要素方面连接的还不够,不智能,体现在两个...
    jiaoqunbao阅读 375评论 0 0
  • 42条:多用GCD,少用performSelector系列方法 1.performSelector方法动态调用方法...
    8fe8946fa366阅读 93评论 0 0