栈+单向链表实现Angular 11访客浏览脚印

Posted 街头小贩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了栈+单向链表实现Angular 11访客浏览脚印相关的知识,希望对你有一定的参考价值。

应用中需要浏览脚印功能实现导航条的后退,登录成功后的跳转,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;

附图:

以上是关于栈+单向链表实现Angular 11访客浏览脚印的主要内容,如果未能解决你的问题,请参考以下文章

[C语言]C语言实现栈(基于单向链表)

[C语言]C语言实现栈(基于单向链表)

Java新手入门200例126之用单向链表实现栈

用Java语言实现单向链表

Java 单向链表翻转

2017/03/31学习笔记