首页 > 其他分享 > 笔记 | Angular 实现 keep-alive (路由复用)

笔记 | Angular 实现 keep-alive (路由复用)

时间:2024-09-03 15:38:09浏览次数:10  
标签:return ActivatedRouteSnapshot url route alive keep 快照 Angular 路由

Angular 的路由复用策略(RouteReuseStrategy)是一种用于优化路由跳转性能和提高用户体验的机制。通过实现RouteReuseStrategy接口,后可以自定义路由的复用行为,避免不必要的组件销毁和重建,同时保持组件的状态。

以下是对Angular路由复用策略的详细介绍:

一、基本概念

RouteReuseStrategy是 Angular 路由模块提供的一个接口,用于控制路由的复用逻辑。当路由切换时,如果目标路由与当前路由使用相同的组件,通过实现这个接口可以避免组件的重新创建,而是复用现有的组件实例。

二、主要作用

  1. 避免不必要的组件销毁和重建:

通过复用组件实例,可以减少 DOM 操作和组件生命周期钩子的调用次数,从而提高应用的性能。

  1. 保持组件状态:

在路由切换时,如果组件被复用,那么它的状态(如表单输入、滚动位置等)也会被保留,从而提升用户体验。

三、解决存在的问题(使用场景)

在当前的安庆项目中,我们采用了 Cesium 库来构建并加载三维场景。开发中,我们注意到一个用户体验上的显著痛点:每当页面发生跳转并重新返回至 Cesium 场景时,都需要重新进行整个三维场景的加载,这一过程不仅延长了用户的等待时间,降低了整体应用的流畅性,还额外增加了系统资源的消耗,对设备的性能提出了更高要求。

为了解决这个问题,计划采取一种优化策略:将 Cesium 实例化的页面进行 keep-alive 处理。通过这一技术手段,我们可以确保在页面跳转时,Cesium 场景及其加载的所有资源(如地形数据、模型等)能够保持活跃状态,而非被销毁并重新加载。这样,当用户再次访问该页面时,能够立即看到之前已经加载完成的场景,无需经历冗长的加载过程,从而显著提升应用的响应速度和用户体验。

四、实现方法

实现核心类:

实现 RouteReuseStrategy

  • shouldDetach()是否允许复用路由
  • store()当路由离开时会触发,存储路由
  • shouldAttach() 是否允许还原路由
  • retrieve()获取存储路由
  • shouldReuseRoute()进入路由触发,是否同一路由时复用路由

流程:

  1. 使用 shouldDetach()把路由 /home 设置为允许复用;
  2. 然后通过 store()将路由快照储存起来;
  3. 路由切换时会触发 shouldReuseRoute()如果返回为真,(即:再次遇到 /home 路由后表示需要复用路由);
  4. 使用 shouldAttach() 检查是否允许还原;
  5. 最后通过 retrieve()拿到路由快照并构建组件。
import { Injectable } from '@angular/core';

import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';

type MyDetachedRouteHandle = DetachedRouteHandle | null;
@Injectable()
export class MyReuseStrategy implements RouteReuseStrategy {
    private static routeCache = new Map<string, DetachedRouteHandle>();

    private static waitDelete: string | null; // 待删除的快照

    /**
     * 用于删除路由快照
     *
     * @param {string} url - 需要删除路由的 URL。
     * @return {void}
     */
    public static deleteRouteSnapshot(url: string): void {
        if (url[0] === '/') {
            url = url.substring(1);
        }
        url = url.replace(/\//g, '_');
        if (MyReuseStrategy.routeCache.has(url)) {
            MyReuseStrategy.routeCache.delete(url);
        }
        MyReuseStrategy.waitDelete = url;
    }

    /**
     * 用于清空路由快照
     */
    public static clearRouteSnapshot(): void {
        MyReuseStrategy.routeCache.clear();
        MyReuseStrategy.waitDelete = null;
    }

    /**
     * 进入路由时触发,根据将来和当前路由配置和参数的比较确定当前路由是否应该重用。
     *
     * @param {ActivatedRouteSnapshot} future - 将来的路由快照。
     * @param {ActivatedRouteSnapshot} curr - 当前路由快照。
     * @return {boolean} 如果应该重用路由,则返回 true,否则返回 false。
     */
    public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return (
            future.routeConfig === curr.routeConfig &&
            JSON.stringify(future.params) === JSON.stringify(curr.params)
        );
    }

    /**
     * 基于路由数据确定路由是否应该分离。
     * 表示对所有路由允许复用 如果有路由不想利用可以在这加一些业务逻辑判断,这里判断路由是否有 keepAlive 数据判断是否复用。
     *
     * @param {ActivatedRouteSnapshot} route - 要检查分离的路由快照。
     * @return {boolean} 如果应该分离路由则返回true,否则返回 false。
     */
    public shouldDetach(route: ActivatedRouteSnapshot): boolean {
        return route.data.keepAlive;
    }

    /**
     * 当路由离开时会触发, 按 path 作为 key 存储路由快照组件当前实例对象
     * 如果路由配置为‘’,则出现Cannot reattach ActivatedRouteSnapshot created from a different route问题
     *
     * @param {ActivatedRouteSnapshot} route - 用于获取完整路由 URL 的路由快照。
     * @param {DetachedRouteHandle} handle - 要存储的路由处理程逻辑。
     * @return {void}
     */
    public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        const url = this.getFullRouteUrl(route);
        if (MyReuseStrategy.waitDelete && MyReuseStrategy.waitDelete === url) {
            // 如果待删除是当前路由,且未存储过则不存储快照
            MyReuseStrategy.waitDelete = null;
            return;
        }
        MyReuseStrategy.routeCache.set(url, handle);
    }

    /**
     * 根据 URL 确定是否应该附加路由。
     * 若 URL 在缓存中有的都认为允许还原路由
     *
     * @param {ActivatedRouteSnapshot} route - 要检查的路由快照。
     * @return {boolean} 是否应该附加路由。
     */
    public shouldAttach(route: ActivatedRouteSnapshot): boolean {
        const url = this.getFullRouteUrl(route);
        return MyReuseStrategy.routeCache.has(url);
    }

    /**
     * 从缓存中获取快照,若无则返回 null
     *
     * @param {ActivatedRouteSnapshot} route - 要检查的路由快照
     * @return {MyDetachedRouteHandle} MyDetachedRouteHandle
     */
    public retrieve(route: ActivatedRouteSnapshot): MyDetachedRouteHandle {
        const url = this.getFullRouteUrl(route);
        let handle = MyReuseStrategy.routeCache.has(url)
            ? MyReuseStrategy.routeCache.get(url)
            : null;
        handle = handle ? handle : null;
        return handle;
    }

    /**
     * 基于提供的 ActivatedRouteSnapshot 获取完整的路由 URL。
     *
     * @param {ActivatedRouteSnapshot} route - 用于生成完整路由 URL 的 ActivatedRouteSnapshot
     * @return {string} 过滤、连接和替换字符后的完整路由URL。
     */
    private getFullRouteUrl(route: ActivatedRouteSnapshot): string {
        return this.getFullRouteUrlPaths(route).filter(Boolean).join('/').replace(/\//g, '_');
    }

    /**
     * 基于提供的 ActivatedRouteSnapshot 获取完整的路由 URL 的 paths。
     *
     * @param {ActivatedRouteSnapshot} route - 用于生成完整路由 URL 的 ActivatedRouteSnapshot
     * @return {string []}
     */
    private getFullRouteUrlPaths(route: ActivatedRouteSnapshot): string[] {
        const paths = route.url.map(urlSegment => urlSegment.path);
        return route.parent ? [...this.getFullRouteUrlPaths(route.parent), ...paths] : paths;
    }
}


将核心类注册到模块中:

在 Angular 应用中,需要将自定义的路由复用策略注入到应用的根模块(通常是 AppModule )中。这可以通过在 providers 数组中添加一个提供器来实现;

...
providers: [
        { provide: RouteReuseStrategy, useClass: MyReuseStrategy }
];
...

路由中开心 keepAlive:

{
  path: 'home',
    ...
  data: { keepAlive: true }
}

标签:return,ActivatedRouteSnapshot,url,route,alive,keep,快照,Angular,路由
From: https://www.cnblogs.com/inslog/p/18394713

相关文章

  • Vue 2 与 Vue 3 中 keep-alive 组件的深入解析
    更多内容前往个人网站:孔乙己大叔        在Vue.js的开发中,keep-alive 是一个非常重要的内置组件,它允许我们将组件状态或实例保持在内存中,避免重新渲染和重新创建组件,这对于需要频繁切换但内容较为复杂的组件场景非常有用,如标签页、列表项的详细视图等。Vue2和V......
  • Zookeeper注册中心实战
    Java学习手册+面试指南:https://javaxiaobear.cnSpringCloudZookeeper通过自动配置和绑定到Spring环境和其他Spring编程模型习惯用法,为SpringBoot应用程序提供ApacheZookeeper集成。通过一些简单的注释,您可以快速启用和配置应用程序内的常见模式,并使用Zookeeper构建大型......
  • 在Angular v3以上版本中,怎么获取 HTML 元素的属性值?
    在Angular中,怎么获取HTML元素的属性值?或者说类似js来操作操作html元素的属性1:使用ElementRef使用ElementRef可以直接访问DOM元素,并获取其属性值。父组件(ParentComponent)import{Component,OnInit,ViewChild,ElementRef}from'@angular/core';@Component({......
  • 分布式锁的实现:ZooKeeper 的解决方案
    在分布式系统中,不同的服务或进程需要访问共享资源时,常常需要一种机制来确保在同一时刻只有一个服务或进程能够访问资源。这种机制被称为分布式锁。ZooKeeper,一个为分布式应用提供一致性服务的开源协调服务,提供了一种实现分布式锁的有效方法。ZooKeeper分布式锁的原理ZooKeep......
  • Redis高可用方案:使用Keepalived实现主备双活oZ
    注意:请确保已经安装Redis和keepalived,本文不在介绍如何安装。1、使用版本说明Redis版本:5.0.2Keepalived版本:1.3.5Linux版本:Centos7.9查看Redis版本:/usr/local/redis/bin/redis-cli-v查看Keepalived版本信息:rpm-qa|grepkeepalived或者keepalived-v2、功能实......
  • Redis高可用方案:使用Keepalived实现主备双活
    注意:请确保已经安装Redis和keepalived,本文不在介绍如何安装。1、使用版本说明Redis版本:5.0.2Keepalived版本:1.3.5Linux版本:Centos7.9查看Redis版本:/usr/local/redis/bin/redis-cli-v查看Keepalived版本信息:rpm-qa|grepkeepalived或者keepalived-v2、功能实......
  • Angular路由RouterLink指令和Router
      RouterLink指令RouterLink的属性[queryParams](查询参数)用法:<a[routerLink]="['/danone']"[queryParams]="{id:1,name:'abc',date:'2020-6-19'}"[state]="{tracingId:123}">达能</a> url......
  • Redis高可用方案:使用Keepalived实现主备双活
    注意:请确保已经安装Redis和keepalived,本文不在介绍如何安装。1、使用版本说明Redis版本:5.0.2Keepalived版本:1.3.5Linux版本:Centos7.9查看Redis版本:/usr/local/redis/bin/redis-cli-v查看Keepalived版本信息:rpm-qa|grepkeepalived或者keepalived-v2、功能实现......
  • keepalived-双主机模式
    keepalived-双主机模式:说明:仅演示VIP地址流动工作模式:主主模式-主/备(虚拟路由器1)备/主(虚拟路由器2)拓扑: 环境说明:1、ka1和ka2分别是2台keepalived服务器2、配置一个2主虚拟路由器,实现两个vrrp实例,互为主备 示例:双主机模式实现VIP地址流动1、ka1keepaliv......
  • keepalived-单主机模式
    keepalived-单主机模式:说明:仅演示VIP地址流动工作模式:主备模式-单虚拟路由拓扑:   环境说明:1、ka1和ka2分别是2台keepalived服务器2、配置一个单主虚拟路由器,实现一个vrrp实例 示例:单主机模式实现VIP地址流动1、ka1keepalived配置:[root@lvs-ka1keepali......