【JS】栈+单向链表实现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;

}

附图:
【JS】栈+单向链表实现Angular 11访客浏览脚印

以上是 【JS】栈+单向链表实现Angular 11访客浏览脚印 的全部内容, 来源链接: utcz.com/a/100469.html

回到顶部