利用中须要浏览足迹性能实现导航条的后退,登录胜利后的跳转,404页面中的:返回上一页性能。当浏览时(非后退操作时)将数据压入栈, 后退时弹出栈顶; 用单向链表来存储数据,应用:ngx-webstorage-service将数据存储在客户端。数据结据为:
//单向链表export interface TrackItem { //上一页的连贯 previous: string; //当前页的连贯 value: string;}
A: 保留
import { ChangeDetectionStrategy, Component, OnInit} from '@angular/core';import { NavigationEnd, Router } from '@angular/router';@Component({ selector: 'app-root', templateUrl: './app.component.html', styles: [``], changeDetection: ChangeDetectionStrategy.Default})export class AppComponent implements OnInit { constructor( private router: Router, private footMark: FootmarkTrackService) { this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((event: any) => { //console.log('[App]prev url:', event.url); this.footMark.save(event.url); }); }}
FootmarkTrackService的代码在前面附上
B: 导航的后退
后退通过指令实现,只有a元素的class定义中含有: historyBack
, 404映射的模板示例:
<a href="javascript:;" role="button" class="btn historyBack">上一页</a>
指令定义如下:
import { Directive, HostListener } from '@angular/core';import { Params, Router } from '@angular/router';@Directive({ selector: 'a.historyBack'})export class HistoryBackDirective { private currentURL: string; constructor(private router: Router, private footMark: FootmarkTrackService) { this.currentURL = router.url; } @HostListener('click', ['$event.target']) public backHistory($event: Event): void { let previousURL: string | null = this.footMark.getPrevious(); let data: { path: string, queryParams: Params } = this.footMark.processURL(previousURL || '/home'); this.router.navigate([data.path], { queryParams: data.queryParams }); }}
C: 登录时获取起源: Referer
import { Component, OnInit } from '@angular/core';import { Params, Router } from '@angular/router';@Component({ selector: 'app-login', templateUrl: './login.component.html', styles: [``]})export class LoginComponent implements OnInit { public member: { names: string, pswd: string, redirect: string } = { names: '', pswd: '', redirect: '' }; private previousUrl!: string | null; constructor(private router: Router, private footMark: FootmarkTrackService) {} ngOnInit(): void { this.previousUrl = this.footMark.getReferer(); } //登录胜利后的回调函数 private storeMember(): void { //ETC //解决完后跳转 this.processRedirectURL(this.member.redirect || this.previousUrl); } private processRedirectURL(argDedirectURL: string | null): void { //是否有参数 let redirectURL: string = argDedirectURL || '/home'; let data: { path: string, queryParams: Params } = this.footMark.processURL(redirectURL); this.router.navigate([data.path], { queryParams: data.queryParams }); }}
D: FootmarkTrackService
import { Injectable, Inject } from '@angular/core';import { Params } from '@angular/router';import { StorageService, SESSION_STORAGE } from 'ngx-webstorage-service';@Injectable({ providedIn: 'root'})export class FootmarkTrackService { private ftKey: string = 'ftStack'; //存储会员浏览地址的路线图 //用处: 1)后退性能.正向压栈,后退弹栈; 2)获取以后地址的referer constructor(@Inject(SESSION_STORAGE) private storage: StorageService) { } /** * 保留/压栈 * @param url */ public save(url: string): void { let data: TrackItem[] = []; let lastEle: TrackItem | undefined = undefined; if (this.exist()) { data = this.get(); lastEle = data[data.length - 1]; } //不存在 或 存在但一样 let previousURL: string = lastEle?.value ?? ''; if (previousURL === url) { //后退时会产生; return; } let pr: TrackItem = { previous: previousURL, value: url }; data.push(pr); this.storage.set(this.ftKey, data); } /** * 是否疏忽地址 * :/member/login(|register|offline); :/404 * @param url * @returns */ private isIgnoreURL(url: string): boolean { return url.startsWith('/member/login') || url.startsWith('/member/register') || url.startsWith('/member/offline') || url.startsWith('/404'); } /** * 是否是第一次保留/栈是否存在 * @returns */ private exist(): boolean { return this.storage.has(this.ftKey); } /** * (2)获取以后地址的Referer * 留神:LoginComponent.ngOnInit办法中调用;若在constructor办法中调用会取到谬误的值 * @returns */ public getReferer(): string | null { if (!this.exist()) { return null; } // let data: TrackItem[] = this.get(); //栈顶 let lastEle: TrackItem | undefined = data[data.length - 1]; return lastEle?.previous ?? null; } /** * 返回存储的数组 * @returns */ private get(): TrackItem[] { return this.storage.get(this.ftKey); } /** * (1)返回前一个地址 * 留神:办法存在一个缺点, 例:1>A->login, 2>login->A 此时调用又回到了A,产生在A(2>)上调用回退无作用的假象.getPreviousRef办法修复此缺点 * @returns */ public getPrevious(): string | null { if (!this.exist()) { return null; } let data: TrackItem[] = this.get(); //弹栈 let result: string | null = null; do { let lastEle: TrackItem | undefined = data.pop(); if (lastEle && typeof (lastEle.previous) !== 'undefined') { result = lastEle.previous; if (this.isIgnoreURL(result)) { result = null; } } } while (result === null); //笼罩掉 this.storage.set(this.ftKey, data); return result; } /** * (1)查看以参考地址为界的前一个地址 * 修复getPrevious办法在疏忽地址前后调用后退无作用的假像 * @param refUrl * @returns */ public getPreviousRef(refUrl: string): string | null { if (!this.exist()) { return null; } let data: TrackItem[] = this.get(); //地址最初一次呈现在哪 let lastShowIndex: number = -1; for (let i: number = data.length - 1; i >= 0; i--) { if (data[i].previous === refUrl) { lastShowIndex = i; break; } } //呈现过后,开始截取前局部 if(lastShowIndex > 0){ data = data.slice(0, lastShowIndex); } //往前推一级 let lastEle: TrackItem | undefined = data.pop(); let result: string | null = lastEle?.previous ?? null; //若是疏忽的地址再往上推一层 if (result !== null && this.isIgnoreURL(result)) { result = data.pop()?.previous ?? null; } //笼罩掉 this.storage.set(this.ftKey, data); return result; } //解决redirect public processURL(redirectURL: string): { path: string, queryParams: Params } { let p: any; let qs: Params = {}; if (redirectURL.indexOf('?') == -1) { p = redirectURL; } else { p = redirectURL.substring(0, redirectURL.indexOf('?')); let queryString = redirectURL.substring(redirectURL.indexOf('?') + 1); if (queryString) { let segment: string[] = queryString.split('&'); segment.forEach(ele => { let kv: string[] = ele.split('='); if (kv.length == 2) { qs[kv[0]] = kv[1]; } }); } } return { path: p, queryParams: qs }; }}//单向链表export interface TrackItem { //上一页的连贯 previous: string; //当前页的连贯 value: string;}
附图: