利用中须要浏览足迹性能实现导航条的后退,登录胜利后的跳转,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;
}
附图: