栈+单向链表实现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访客浏览脚印的主要内容,如果未能解决你的问题,请参考以下文章