栈+单向链表实现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;}//处理redirectpublic 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;
}

附图:
在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部