发表于: 2020-07-08 21:00:33

1 2323


今天完成的事情:今天写了路由守卫还是很好用的
明天计划的事情:继续后续的任务
遇到的问题:今天忘记声明

providers: [AuthGuard]

导致用不出来,后续还是要熟练应用
收获:

路由守卫

守卫,顾名思义,必须满足一定的条件得到许可方可通行,否则拒绝访问或者重定向。Angular中路由守卫可以借此处理一些权限问题,通常应用中存储了用户登录和用户权限信息,遇到路由导航时会进行验证是否可以跳转。

4种守卫类型

按照触发顺序依次为:canload(加载)、canActivate(进入)、canActivateChild(进入子路由)和canDeactivate(离开)。

一个所有守卫都是通过的守卫类:

import { Injectable } from '@angular/core';
import {
    CanActivate,
    Router,
    ActivatedRouteSnapshot,
    RouterStateSnapshot,
    CanActivateChild,
    CanLoad,
    CanDeactivate
from '@angular/router';
import { Route } from '@angular/compiler/src/core';
import { NewsComponent } from '../component/news/news.component';


@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivateCanActivateChildCanLoadCanDeactivate<any> {
    constructor(
        private routerRouter
    ) {

    }
    canActivate(routeActivatedRouteSnapshotstateRouterStateSnapshot): boolean {
        // 权限控制逻辑如 是否登录/拥有访问权限
        console.log('canActivate');
        return true;
    }
    canDeactivate(
        componentNewsComponent,
        currentRouteActivatedRouteSnapshot,
        currentStateRouterStateSnapshot,
        nextStateRouterStateSnapshot) {
        console.log('canDeactivate');
        return true;
    }
    canActivateChild() {
        // 返回false则导航将失败/取消
        // 也可以写入具体的业务逻辑
        console.log('canActivateChild');
        return true;
    }
    canLoad(routeRoute) {
        // 是否可以加载路由
        console.log('canload');
        return true;
    }
}

app-routing.module.ts

import { NgModule } from '@angular/core';
import { RoutesRouterModule } from '@angular/router';
import { ErrorComponent } from './error/error.component';
import { AuthGuard } from './core/auth-guard';

const routesRoutes = [
    // 一般情况很少需要同时写多个守卫,如果有也是分开几个文件(针对复杂场景,否则一般使用canActivated足够)
    {
        path: '',
        canLoad: [AuthGuard],
        canActivate: [AuthGuard],
        canActivateChild: [
            AuthGuard
        ],
        canDeactivate: [AuthGuard],
        loadChildren: () => import('./pages/pages.module').then(m => m.PagesModule)
    },
    {
        path: 'error',
        component: ErrorComponent,
        data: {
            title: '参数错误或者地址不存在'
        }
    },
    {
        path: '**',
        redirectTo: 'error',
        pathMatch: 'full'
    }
];

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

使用场景分析

1.canLoad

默认值为true,表明路由是否可以被加载,一般不会认为控制这个守卫逻辑,99.99%情况下,默认所有app模块下路由均允许canLoad

2.canActivate

是否允许进入该路由,此场景多为权限限制的情况下,比如客户未登录的情况下查询某些资料页面,在此方法中去判断客户是否登陆,如未登录则强制导航到登陆页或者提示无权限,即将返回等信息提示。

3.canActivateChild

是否可以导航子路由,同一个路由不会同时设置canActivate为true,canActivateChild为false的情况,此外,这个使用场景很苛刻,尤其是懒加载路由模式下,暂时未使用到设置为false的场景。

4.CanDeactivate

路由离开的时候进行触发的守卫,使用场景比较经典,通常是某些页面比如表单页面填写的内容需要保存,客户突然跳转其它页面或者浏览器点击后退等改变地址的操作,可以在守卫中增加弹窗提示用户正在试图离开当前页面,数据还未保存 等提示。

场景模拟

登录判断

前期准备:login组件;配置login路由

因为login是独立一个页面,所以app.component.html应该只会剩余一个路由导航

<!-- NG-ZORRO -->
<router-outlet></router-outlet>

取而代之的是pages.component.html页面中要加入header和footer部分变为如下:

<app-header></app-header>
<div nz-row class="main">
 <div nz-col nzSpan="24">
  <router-outlet></router-outlet>
 </div>
</div>
<app-footer></app-footer>

app-routing.module.ts 中路由配置2种模式分析:

// 非懒加载模式
import { NgModule } from '@angular/core';
import { RoutesRouterModule } from '@angular/router';
import { ErrorComponent } from './error/error.component';
import { AuthGuard } from './core/auth-guard';
import { LoginComponent } from './component/login/login.component';
import { PagesComponent } from './pages/pages.component';
import { IndexComponent } from './component/index/index.component';

const routesRoutes = [
    // 一般情况很少需要同时写多个守卫,如果有也是分开几个文件(针对复杂场景,否则一般使用canActivated足够)
    {
        path: '',
        canLoad: [AuthGuard],
        canActivate: [AuthGuard],
        canActivateChild: [
            AuthGuard
        ],
        canDeactivate: [AuthGuard],
        component: PagesComponent,
        children: [
            {
                path: 'index',
                component: IndexComponent
            }
            // ...
        ]
        // loadChildren: () => import('./pages/pages.module').then(m => m.PagesModule)
    },
    {
        path: 'login',
        component: LoginComponent,
        data: {
            title: '登录'
        }
    },
    {
        path: 'error',
        component: ErrorComponent,
        data: {
            title: '参数错误或者地址不存在'
        }
    },
    {
        path: '**',
        redirectTo: 'error',
        pathMatch: 'full'
    }
];

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

非懒加载模式下,想要pages组件能够正常显示切换的路由和固定头部足部,路由只能像上述这样配置,也就是所有组件都在app模块中声明,显然不是很推荐这种模式,切换回懒加载模式:

{
    path'',
        canLoad: [AuthGuard],
            canActivate: [AuthGuard],
                canActivateChild: [
                    AuthGuard
                ],
                    canDeactivate: [AuthGuard],
                        loadChildren: () => import('./pages/pages.module').then(m => m.PagesModule)
},

pages-routing.module.ts

初始模板:

const routesRoutes = [
    {
        path: '',
        redirectTo: 'index',
        pathMatch: 'full'
    },
    {
        path: 'index',
        component: IndexComponent,
        data: {
            title: '公司主页'
        }
    },
    {
        path: 'about',
        component: AboutComponent,
        data: {
            title: '关于我们'
        }
    },
    {
        path: 'contact',
        component: ContactComponent,
        data: {
            title: '联系我们'
        }
    },
    {
        path: 'news',
        canDeactivate: [AuthGuard],
        loadChildren: () => import('../component/news/news.module').then(m => m.NewsModule)
    },
]

浏览器截图:

明明我们的html写了头部和底部组件却没显示?

路由的本质:根据配置的path路径去加载组件或者模块,此处我们是懒加载了路由,根据路由模块再去加载不同组件,唯独缺少了加载了pages组件,其实理解整个并不难,index.html中有个<app-root></app-root>,这就表明app组件被直接插入了dom中,反观pages组件,根本不存在直接插进dom的情况,所以这个组件根本没被加载,验证我们的猜想很简单:

export class PagesComponent implements OnInit {
 
 constructor() { }
 
 ngOnInit() {
  alert();
 }
 
}

经过刷新页面,alert()窗口并没有出现~,可想而知,直接通过路由模块去加载了对应组件;其实我们想要的效果就是之前改造前的app.component.html效果,所以路由配置要参照更改:

const routesRoutes = [
    {
        path: '',
        component: PagesComponent,
        children: [
            {
                path: '',
                redirectTo: 'index',
                pathMatch: 'full'
            },
            {
                path: 'index',
                component: IndexComponent,
                data: {
                    title: '公司主页'
                }
            },
            {
                path: 'about',
                component: AboutComponent,
                data: {
                    title: '关于我们'
                }
            },
            {
                path: 'contact',
                component: ContactComponent,
                data: {
                    title: '联系我们'
                }
            },
            {
                path: 'news',
                canDeactivate: [AuthGuard],
                loadChildren: () => import('../component/news/news.module').then(m => m.NewsModule)
            },
        ]
    }
];

这样写,pages组件就被加载了,重回正题,差点回不来,我们在登录组件中写了简单的登录逻辑:

import { ComponentOnInit } from '@angular/core';
import { FormGroupFormControlValidatorsFormBuilder } from '@angular/forms';
import { Router } from '@angular/router';

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
    loginFormFormGroup;
    constructor(
        private fbFormBuilder,
        private routerRouter
    ) { }

    ngOnInit() {
        this.loginForm = this.fb.group({
            loginName: ['', [Validators.required]],
            password: ['', [Validators.required]]
        });
        console.log(this.loginForm);
    }

    loginSubmit(eventvalue) {
        if (this.loginForm.valid) {
            window.localStorage.setItem('loginfo'JSON.stringify(this.loginForm.value));
            this.router.navigateByUrl('index');
        }
    }
}

守卫中:

canActivate(routeActivatedRouteSnapshotstateRouterStateSnapshot): boolean {
    // 权限控制逻辑如 是否登录/拥有访问权限
    console.log('canActivate'route);
    const isLogin = window.localStorage.getItem('loginfo') ? true : false;
    if (!isLogin) {
        console.log('login');
        this.router.navigateByUrl('login');
    }
    return true;
}

进入的守卫就做好了

剩下的明天继续


返回列表 返回列表
评论

    分享到