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

附图: