programing

Angular 2의 특정 루트에 대해 RouteReuseStrategy shouldDetach를 구현하는 방법

easyjava 2023. 3. 5. 10:28
반응형

Angular 2의 특정 루트에 대해 RouteReuseStrategy shouldDetach를 구현하는 방법

Angular 2 모듈이 있어 루팅을 구현하고 있으며 탐색 시 상태를 저장해야 합니다.
사용자는 다음을 수행할 수 있어야 합니다.

  1. '검색 수식'을 사용하여 문서 검색
  2. 결과 중 하나로 이동하다
  3. 서버와 통신하지 않고 '검색 결과'로 돌아갑니다.

수 있어요.RouteReuseStrategy
'하다, 하다' 입니다.
문서가 저장되지 않도록 구현하려면 어떻게 해야 합니까?

따라서 경로 "documents" 상태는 저장되어야 하며 경로 "documents/:id" 상태는 저장되지 않아야 합니다.

앤더스, 좋은 질문이야!

저는 당신과 거의 같은 사용 사례를 가지고 있고, 같은 일을 하고 싶었습니다![ User search ]> [ get results ]> [ User navigations ]> [ User navigations back ]> [ BOOM ]의 빠른 결과입니다만, 유저가 네비게이트 한 특정의 결과는 보존하고 싶지 않습니다.

dr;dr

수업을 꼭 해야 합니다.RouteReuseStrategy해 주세요.ngModule되었을 때 [ ]를 shouldDetach★★★★★★★★★★★★★★★★★★★★★★★★★.true합니다., Angular는 경로를 저장합니다.되어 있는 는, .shouldAttach★★★★★★★★★★★★★★★★★shouldAttachtrue를 반환합니다.Angular는 요청된 경로 대신 저장된 경로를 사용합니다.여기 가지고 놀 수 있는 플런커가 있습니다.

RouteReuse Strategy에 대해서

이 질문을 하면 RouteReuseStrategy를 통해 Angular에게 컴포넌트를 파기하지 않고 나중에 재렌더링하기 위해 저장할 수 있음을 알 수 있습니다.다음과 같은 이점이 있습니다.

  • 서버 호출 감소
  • 고속화
  • 그리고 컴포넌트는 디폴트로 그대로 렌더링됩니다.

예를 들어 사용자가 많은 텍스트를 입력했는데도 페이지를 일시적으로 남기고 싶다면 마지막 항목이 중요합니다.폼의 양이 너무 많기 때문에 엔터프라이즈 어플리케이션에서는 이 기능을 매우 좋아합니다.

이것이 내가 문제를 해결하기 위해 생각해 낸 것이다.말했지만, 이렇게 돼요.RouteReuseStrategy버전 3.4.1 이후에서는 @filename/filename에 의해 제공되고 있습니다.

하기 위해서

먼저 프로젝트에 @angular/router 버전 3.4.1 이상이 있는지 확인합니다.

그런 다음 구현된 클래스를 저장할 파일을 만듭니다.RouteReuseStrategy는 제 거 reuse-strategy.ts을 에에배 the the the에 넣었습니다./app보관을 위한 폴더입니다.현재 이 클래스는 다음과 같습니다.

import { RouteReuseStrategy } from '@angular/router';

export class CustomReuseStrategy implements RouteReuseStrategy {
}

(TypeScript 오류에 대해 걱정하지 마십시오. 이제 곧 모든 문제를 해결할 수 있습니다.)

수강생에게 수업을 제공함으로써 기초 작업을 마칩니다.app.module ''라고 쓰지 해 주세요.CustomReuseStrategy , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,.importreuse-strategy.ts럼에그 한 also도.import { RouteReuseStrategy } from '@angular/router';

@NgModule({
    [...],
    providers: [
        {provide: RouteReuseStrategy, useClass: CustomReuseStrategy}
    ]
)}
export class AppModule {
}

마지막 부분은 루트의 분리, 저장, 검색 및 재접속 여부를 제어하는 클래스를 작성하는 것입니다.이전 복사/붙여넣기에 앞서, 여기서 기계 구조에 대해 제가 이해하고 있는 대로 간단히 설명하겠습니다.제가 설명하는 방법에 대해서는 아래의 코드를 참조해 주십시오.물론 코드에는 많은 문서가 있습니다.

  1. 할 는, 「 」shouldReuseRoute하지만, 이 돌아온다면true그러면 현재 사용 중인 경로가 실제로 재사용되고 다른 메서드는 실행되지 않습니다.거짓
  2. ifshouldReuseRoutefalse,shouldDetachshouldDetach루트 하고 "경로 저장"을 한다.boolean그만큼을 나타냅니다.여기서 패스를 저장할지 저장하지 않을지를 결정해야 합니다. 경우 저장할 패스의 배열을 체크합니다.route.routeConfig.pathpath이치노
  3. ifshouldDetachtrue,store이 경우 루트에 대해 필요한 정보를 저장할 수 있습니다. 일이 있어도 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★DetachedRouteHandleAngular가 나중에 저장된 구성요소를 식별하기 위해 사용하는 것이기 때문입니다.아래에, 나는 두 가지 모두를 저장한다.DetachedRouteHandleActivatedRouteSnapshot우리 반의 변수로요

지금까지 스토리지에 대한 논리를 살펴봤지만 구성 요소로 이동하는 것은 어떨까요?Angular는 어떻게 당신의 네비게이션을 가로채고 저장된 네비게이션을 제자리에 두기로 결정합니까?

  1. 한 번 말하다shouldReuseRoutefalse,shouldAttach.display 내의 할지 여부를 할 수 입니다. 이것은 컴포넌트를 재생성할지 메모리에서 사용할지 여부를 판단할 수 있는 기회입니다.컴포넌트를 하십시오.true당신 방식대로 잘 지내세요!그리고 넌 잘 가고 있어!
  2. 이제 각 구성 요소를 "우리는 어떤 구성 요소를 사용하시겠습니까?" " 제 포 느 습 니 겠 시 하 용 까 now angular? do"' to will사 want will which indicate you,라고 묻습니다.이 컴포넌트를 반환함으로써 이 컴포넌트를 나타냅니다.DetachedRouteHandle부에서retrieve.

그게 필요한 논리입니다!이 정도면 충분해!드:의 코드:reuse-strategy.ts아래에는 두 개체를 비교할 수 있는 니프티 함수도 있습니다.할 때 합니다.route.params ★★★★★★★★★★★★★★★★★」route.queryParams저장해 둔 것과 함께요만약 그것들이 모두 일치한다면, 나는 새로운 컴포넌트를 생성하지 않고 저장된 컴포넌트를 사용하고 싶다.하지만 어떻게 하는지는 너에게 달렸어!

reuse-builts.ts를 참조해 주세요.

/**
 * reuse-strategy.ts
 * by corbfon 1/6/17
 */

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

/** Interface for object which can store both: 
 * An ActivatedRouteSnapshot, which is useful for determining whether or not you should attach a route (see this.shouldAttach)
 * A DetachedRouteHandle, which is offered up by this.retrieve, in the case that you do want to attach the stored route
 */
interface RouteStorageObject {
    snapshot: ActivatedRouteSnapshot;
    handle: DetachedRouteHandle;
}

export class CustomReuseStrategy implements RouteReuseStrategy {

    /** 
     * Object which will store RouteStorageObjects indexed by keys
     * The keys will all be a path (as in route.routeConfig.path)
     * This allows us to see if we've got a route stored for the requested path
     */
    storedRoutes: { [key: string]: RouteStorageObject } = {};

    /** 
     * Decides when the route should be stored
     * If the route should be stored, I believe the boolean is indicating to a controller whether or not to fire this.store
     * _When_ it is called though does not particularly matter, just know that this determines whether or not we store the route
     * An idea of what to do here: check the route.routeConfig.path to see if it is a path you would like to store
     * @param route This is, at least as I understand it, the route that the user is currently on, and we would like to know if we want to store it
     * @returns boolean indicating that we want to (true) or do not want to (false) store that route
     */
    shouldDetach(route: ActivatedRouteSnapshot): boolean {
        let detach: boolean = true;
        console.log("detaching", route, "return: ", detach);
        return detach;
    }

    /**
     * Constructs object of type `RouteStorageObject` to store, and then stores it for later attachment
     * @param route This is stored for later comparison to requested routes, see `this.shouldAttach`
     * @param handle Later to be retrieved by this.retrieve, and offered up to whatever controller is using this class
     */
    store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        let storedRoute: RouteStorageObject = {
            snapshot: route,
            handle: handle
        };

        console.log( "store:", storedRoute, "into: ", this.storedRoutes );
        // routes are stored by path - the key is the path name, and the handle is stored under it so that you can only ever have one object stored for a single path
        this.storedRoutes[route.routeConfig.path] = storedRoute;
    }

    /**
     * Determines whether or not there is a stored route and, if there is, whether or not it should be rendered in place of requested route
     * @param route The route the user requested
     * @returns boolean indicating whether or not to render the stored route
     */
    shouldAttach(route: ActivatedRouteSnapshot): boolean {

        // this will be true if the route has been stored before
        let canAttach: boolean = !!route.routeConfig && !!this.storedRoutes[route.routeConfig.path];

        // this decides whether the route already stored should be rendered in place of the requested route, and is the return value
        // at this point we already know that the paths match because the storedResults key is the route.routeConfig.path
        // so, if the route.params and route.queryParams also match, then we should reuse the component
        if (canAttach) {
            let willAttach: boolean = true;
            console.log("param comparison:");
            console.log(this.compareObjects(route.params, this.storedRoutes[route.routeConfig.path].snapshot.params));
            console.log("query param comparison");
            console.log(this.compareObjects(route.queryParams, this.storedRoutes[route.routeConfig.path].snapshot.queryParams));

            let paramsMatch: boolean = this.compareObjects(route.params, this.storedRoutes[route.routeConfig.path].snapshot.params);
            let queryParamsMatch: boolean = this.compareObjects(route.queryParams, this.storedRoutes[route.routeConfig.path].snapshot.queryParams);

            console.log("deciding to attach...", route, "does it match?", this.storedRoutes[route.routeConfig.path].snapshot, "return: ", paramsMatch && queryParamsMatch);
            return paramsMatch && queryParamsMatch;
        } else {
            return false;
        }
    }

    /** 
     * Finds the locally stored instance of the requested route, if it exists, and returns it
     * @param route New route the user has requested
     * @returns DetachedRouteHandle object which can be used to render the component
     */
    retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {

        // return null if the path does not have a routerConfig OR if there is no stored route for that routerConfig
        if (!route.routeConfig || !this.storedRoutes[route.routeConfig.path]) return null;
        console.log("retrieving", "return: ", this.storedRoutes[route.routeConfig.path]);

        /** returns handle when the route.routeConfig.path is already stored */
        return this.storedRoutes[route.routeConfig.path].handle;
    }

    /** 
     * Determines whether or not the current route should be reused
     * @param future The route the user is going to, as triggered by the router
     * @param curr The route the user is currently on
     * @returns boolean basically indicating true if the user intends to leave the current route
     */
    shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        console.log("deciding to reuse", "future", future.routeConfig, "current", curr.routeConfig, "return: ", future.routeConfig === curr.routeConfig);
        return future.routeConfig === curr.routeConfig;
    }

    /** 
     * This nasty bugger finds out whether the objects are _traditionally_ equal to each other, like you might assume someone else would have put this function in vanilla JS already
     * One thing to note is that it uses coercive comparison (==) on properties which both objects have, not strict comparison (===)
     * Another important note is that the method only tells you if `compare` has all equal parameters to `base`, not the other way around
     * @param base The base object which you would like to compare another object to
     * @param compare The object to compare to base
     * @returns boolean indicating whether or not the objects have all the same properties and those properties are ==
     */
    private compareObjects(base: any, compare: any): boolean {

        // loop through all properties in base object
        for (let baseProperty in base) {

            // determine if comparrison object has that property, if not: return false
            if (compare.hasOwnProperty(baseProperty)) {
                switch(typeof base[baseProperty]) {
                    // if one is object and other is not: return false
                    // if they are both objects, recursively call this comparison function
                    case 'object':
                        if ( typeof compare[baseProperty] !== 'object' || !this.compareObjects(base[baseProperty], compare[baseProperty]) ) { return false; } break;
                    // if one is function and other is not: return false
                    // if both are functions, compare function.toString() results
                    case 'function':
                        if ( typeof compare[baseProperty] !== 'function' || base[baseProperty].toString() !== compare[baseProperty].toString() ) { return false; } break;
                    // otherwise, see if they are equal using coercive comparison
                    default:
                        if ( base[baseProperty] != compare[baseProperty] ) { return false; }
                }
            } else {
                return false;
            }
        }

        // returns true only after false HAS NOT BEEN returned through all loops
        return true;
    }
}

행동

이 실장에서는 사용자가 방문하는 모든 고유 루트가 라우터에 정확히1회 저장됩니다.이렇게 하면 사이트의 사용자 세션 내내 메모리에 저장된 구성 요소가 계속 추가됩니다.그는 '경로 제한'입니다.shouldDetach저장할 경로를 제어합니다.

를 들어에서 무언가를 합니다.search/:term 나올 수 www.yourwebsite.com/search/thingsearchedfor검색 페이지에는 다수의 검색 결과가 포함되어 있습니다.이 경로를 저장해야 합니다. 아아아! 결과를 결과를 볼 수 있습니다.view/:resultId보관하고 싶지 않으시겠지만, 아마 한 번만 있을 겁니다.상기의 실장이 실시되고 있는 경우, 저는 단순히,shouldDetach★★★★★★★★★는 다음과 같이 표시됩니다예를 들어 다음과 같습니다.

먼저 저장할 경로를 배열합니다.

private acceptedRoutes: string[] = ["search/:term"];

이제 ,, 제에서shouldDetach 확인해보겠습니다.route.routeConfig.path츠미야

shouldDetach(route: ActivatedRouteSnapshot): boolean {
    // check to see if the route's path is in our acceptedRoutes array
    if (this.acceptedRoutes.indexOf(route.routeConfig.path) > -1) {
        console.log("detaching", route);
        return true;
    } else {
        return false; // will be "view/:resultId" when user navigates to result
    }
}

Angular는 루트의 인스턴스를 하나만 저장하므로 이 스토리지는 가볍고 컴포넌트는 다음 위치에만 저장됩니다.search/:term아, 맞다 른든!!!!!!!!!!!!!!!!

기타 링크

아직 많은 문서가 준비되어 있지는 않지만, 현재 존재하는 것에 대한 몇 가지 링크를 다음에 제시하겠습니다.

각도 문서: https://angular.io/docs/ts/latest/api/router/index/RouteReuseStrategy-class.html

도입부 기사: https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrategy.aspx

nativescript-discript 기본 RouteReuse Strategy 구현:https://github.com/NativeScript/nativescript-angular/blob/cb4fd3a/nativescript-angular/router/ns-route-reuse-strategy.ts

받아들여진 대답에 주눅들지 마세요. 이것은 꽤 간단합니다.필요한 것에 대한 빠른 답변이 있습니다.저는 최소한 인정된 답을 읽어보라고 권하고 싶습니다. 왜냐하면 그것은 매우 상세한 내용이 담겨있기 때문입니다.

이 솔루션에서는 허가된 답변과 같은 파라미터 비교는 이루어지지 않지만 루트 세트를 저장하는 경우에는 정상적으로 동작합니다.

app.ts.imports:

import { RouteReuseStrategy } from '@angular/router';
import { CustomReuseStrategy, Routing } from './shared/routing';

@NgModule({
//...
providers: [
    { provide: RouteReuseStrategy, useClass: CustomReuseStrategy },
  ]})

shared/syslog.ts:

export class CustomReuseStrategy implements RouteReuseStrategy {
 routesToCache: string[] = ["dashboard"];
 storedRouteHandles = new Map<string, DetachedRouteHandle>();

 // Decides if the route should be stored
 shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return this.routesToCache.indexOf(route.routeConfig.path) > -1;
 }

 //Store the information for the route we're destructing
 store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    this.storedRouteHandles.set(route.routeConfig.path, handle);
 }

//Return true if we have a stored route object for the next route
 shouldAttach(route: ActivatedRouteSnapshot): boolean {
    return this.storedRouteHandles.has(route.routeConfig.path);
 }

 //If we returned true in shouldAttach(), now return the actual route data for restoration
 retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    return this.storedRouteHandles.get(route.routeConfig.path);
 }

 //Reuse the route if we're going to and from the same route
 shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
 }
}

(Corbfon에 의해) 인정된 답변과 Chris Fremgen의 보다 간단하고 알기 쉬운 설명 외에 재사용 전략을 사용해야 하는 보다 유연한 경로 처리 방법을 추가하고 싶습니다.

두 응답 모두 캐시할 경로를 배열에 저장한 후 현재 경로 경로가 배열에 있는지 여부를 확인합니다.은 하다, 하다, 하다, 하다에서 합니다.shouldDetach★★★★★★ 。

입니다.루트 이름을 변경하려면 루트 이름도 변경해야 하기 때문입니다.CustomReuseStrategyclass. 개발자가 루트 할 수 .RouteReuseStrategy.

에 수 .RouterModule를 사용합니다.data이렇게 하면 루트 이름을 변경해도 재사용 전략이 적용됩니다.

{
  path: 'route-name-i-can-change',
  component: TestComponent,
  data: {
    reuseRoute: true
  }
}

에 리 and and에shouldDetach그 방법을 활용할 수 있습니다.

shouldDetach(route: ActivatedRouteSnapshot): boolean {
  return route.data.reuseRoute === true;
}

보다 유효하고 완전하며 재사용 가능한 또 다른 구현입니다.이것은 @U'ur Dincs로서 저속 로드된 모듈을 지원하며 @Davor 루트 데이터 플래그를 통합합니다.가장 향상된 기능은 페이지 절대 경로를 기반으로 (거의) 고유 식별자를 자동으로 생성하는 것입니다.이렇게 하면 모든 페이지에서 직접 정의할 필요가 없습니다.

의 페이지를 마크합니다. " " " " "reuseRoute: true . . . . 。shouldDetach★★★★★★ 。

{
  path: '',
  component: MyPageComponent,
  data: { reuseRoute: true },
}

이는 쿼리 매개 변수를 비교하지 않고 가장 간단한 전략 구현입니다.

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

export class CustomReuseStrategy implements RouteReuseStrategy {

  storedHandles: { [key: string]: DetachedRouteHandle } = {};

  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return route.data.reuseRoute || false;
  }

  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    const id = this.createIdentifier(route);
    if (route.data.reuseRoute) {
      this.storedHandles[id] = handle;
    }
  }

  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const id = this.createIdentifier(route);
    const handle = this.storedHandles[id];
    const canAttach = !!route.routeConfig && !!handle;
    return canAttach;
  }

  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    const id = this.createIdentifier(route);
    if (!route.routeConfig || !this.storedHandles[id]) return null;
    return this.storedHandles[id];
  }

  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
  }

  private createIdentifier(route: ActivatedRouteSnapshot) {
    // Build the complete path from the root to the input route
    const segments: UrlSegment[][] = route.pathFromRoot.map(r => r.url);
    const subpaths = ([] as UrlSegment[]).concat(...segments).map(segment => segment.path);
    // Result: ${route_depth}-${path}
    return segments.length + '-' + subpaths.join('/');
  }
}

이치 compareObjects@Corbfon @Corbfon cor보다 다다 다다 다cor corcor corcor 。기본 및 비교 오브젝트의 속성을 모두 루프합니다. lodash와 같은 높은 할 수 .isEqual★★★★★★ 。

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

interface RouteStorageObject {
  snapshot: ActivatedRouteSnapshot;
  handle: DetachedRouteHandle;
}

export class CustomReuseStrategy implements RouteReuseStrategy {

  storedRoutes: { [key: string]: RouteStorageObject } = {};

  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return route.data.reuseRoute || false;
  }

  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    const id = this.createIdentifier(route);
    if (route.data.reuseRoute && id.length > 0) {
      this.storedRoutes[id] = { handle, snapshot: route };
    }
  }

  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const id = this.createIdentifier(route);
    const storedObject = this.storedRoutes[id];
    const canAttach = !!route.routeConfig && !!storedObject;
    if (!canAttach) return false;

    const paramsMatch = this.compareObjects(route.params, storedObject.snapshot.params);
    const queryParamsMatch = this.compareObjects(route.queryParams, storedObject.snapshot.queryParams);

    console.log('deciding to attach...', route, 'does it match?');
    console.log('param comparison:', paramsMatch);
    console.log('query param comparison', queryParamsMatch);
    console.log(storedObject.snapshot, 'return: ', paramsMatch && queryParamsMatch);

    return paramsMatch && queryParamsMatch;
  }

  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    const id = this.createIdentifier(route);
    if (!route.routeConfig || !this.storedRoutes[id]) return null;
    return this.storedRoutes[id].handle;
  }

  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
  }

  private createIdentifier(route: ActivatedRouteSnapshot) {
    // Build the complete path from the root to the input route
    const segments: UrlSegment[][] = route.pathFromRoot.map(r => r.url);
    const subpaths = ([] as UrlSegment[]).concat(...segments).map(segment => segment.path);
    // Result: ${route_depth}-${path}
    return segments.length + '-' + subpaths.join('/');
  }

  private compareObjects(base: any, compare: any): boolean {

    // loop through all properties
    for (const baseProperty in { ...base, ...compare }) {

      // determine if comparrison object has that property, if not: return false
      if (compare.hasOwnProperty(baseProperty)) {
        switch (typeof base[baseProperty]) {
          // if one is object and other is not: return false
          // if they are both objects, recursively call this comparison function
          case 'object':
            if (typeof compare[baseProperty] !== 'object' || !this.compareObjects(base[baseProperty], compare[baseProperty])) {
              return false;
            }
            break;
          // if one is function and other is not: return false
          // if both are functions, compare function.toString() results
          case 'function':
            if (typeof compare[baseProperty] !== 'function' || base[baseProperty].toString() !== compare[baseProperty].toString()) {
              return false;
            }
            break;
          // otherwise, see if they are equal using coercive comparison
          default:
            // tslint:disable-next-line triple-equals
            if (base[baseProperty] != compare[baseProperty]) {
              return false;
            }
        }
      } else {
        return false;
      }
    }

    // returns true only after false HAS NOT BEEN returned through all loops
    return true;
  }
}

내 답변에 대한 코멘트를 남길 수 있는 가장 좋은 키를 생성하는 방법이 있다면 코드를 업데이트하겠습니다.

솔루션을 공유해 주신 모든 분들께 감사드립니다.

Chris Fremgen의 전략을 장해가 적은 모듈에서 사용하려면 CustomReuseStrategy 클래스를 다음과 같이 변경합니다.

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

export class CustomReuseStrategy implements RouteReuseStrategy {
  routesToCache: string[] = ["company"];
  storedRouteHandles = new Map<string, DetachedRouteHandle>();

  // Decides if the route should be stored
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
     return this.routesToCache.indexOf(route.data["key"]) > -1;
  }

  //Store the information for the route we're destructing
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
     this.storedRouteHandles.set(route.data["key"], handle);
  }

  //Return true if we have a stored route object for the next route
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
     return this.storedRouteHandles.has(route.data["key"]);
  }

  //If we returned true in shouldAttach(), now return the actual route data for restoration
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
     return this.storedRouteHandles.get(route.data["key"]);
  }

  //Reuse the route if we're going to and from the same route
  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
     return future.routeConfig === curr.routeConfig;
  }
}

마지막으로 기능 모듈의 라우팅 파일에서 키를 정의합니다.

{ path: '', component: CompanyComponent, children: [
    {path: '', component: CompanyListComponent, data: {key: "company"}},
    {path: ':companyID', component: CompanyDetailComponent},
]}

자세한 것은 이쪽.

Angular 13 (28/02/2022 버전)

많은 가이드와 제안을 읽은 후에.이건 설명할 수 있어요

첫째, 당신은 미래와 미래를 이해해야 한다.

에서 : " " " 에서 네비게이트 합니다.localhost/a로로 합니다.localhost/b제학학 【b】

1: ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★/a -> /b

  • willReuseRoute:falsefuture !== current.
  • Detach : :: :truedetach) 것 ( ) 。store.attach를 참조해 주세요.
  • " " " " 。true || fasehandler 그렇다면attach하지 No」(이 는 「NoNo」)

2: ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」/b?id=1 -> /b?id=2

  • willReuseRoute:truefuture === current;
  • should Detach: 건너뛰기
  • should Retrieve: 건너뛰기

3: "에서 " 3: "에서 "/b -> /a

  • willReuseRoute:falsefuture !== current.
  • Detach : :: :truedetach) 것 ( ) 。store.attach를 참조해 주세요.
  • " " " " 。true || fasehandler 그렇다면attach하지 '그렇다' (이 경우는 '네'입니다.

https://stackoverflow.com/a/45788698/5748537 에서 보다 심플한 비주얼을 보실 수 있습니다.

navigate to a
shouldReuseRoute->return true->do nothing

a->b
shouldReuseRoute()->return false->shouldDetach()->return true->store a

then b->a
shouldReuseRoute()->return false->shouldDetach()->return true->store b->retrieve() return a ->attach() a.

https://stackoverflow.com/a/69004775/5748537에서 훨씬 더 시각적으로 확인할 수 있습니다.

그리고 각 팀의 올바른 코드를 완성합니다.https://github.com/angular/angular/issues/44383

export class CustomRouteReuseStrategy implements RouteReuseStrategy { 
    private handlers: Map<Route, DetachedRouteHandle> = new Map();

    constructor() {}

    public shouldDetach(_route: ActivatedRouteSnapshot): boolean {
        return true;
    }

    public store(
        route: ActivatedRouteSnapshot,
        handle: DetachedRouteHandle
    ): void {
        if (!route.routeConfig) return;
        this.handlers.set(route.routeConfig, handle);
    }

    public shouldAttach(route: ActivatedRouteSnapshot): boolean {
        return !!route.routeConfig && !!this.handlers.get(route.routeConfig);
    }

    public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle|null {
        if (!route.routeConfig || !this.handlers.has(route.routeConfig)) return null;
        return this.handlers.get(route.routeConfig)!;
    }

    public shouldReuseRoute(
        future: ActivatedRouteSnapshot,
        curr: ActivatedRouteSnapshot
    ): boolean {
        return future.routeConfig === curr.routeConfig;
    }
}

우리의 경우 언급된 모든 해결 방법이 다소 불충분했다.다음과 같은 기능을 갖춘 소규모 비즈니스 앱이 있습니다.

  1. 소개 페이지
  2. 로그인 페이지
  3. 앱(로그인 후)

델의 요건:

  1. 느리게 로드된 모듈
  2. 멀티 레벨 루트
  3. 모든 라우터/컴포넌트 상태를 앱 섹션의 메모리에 저장
  4. 특정 경로에서 기본 각도 재사용 전략을 사용하는 옵션
  5. 로그아웃 시 메모리에 저장된 모든 구성 요소 삭제

루트의 간단한 예:

const routes: Routes = [{
    path: '',
    children: [
        {
            path: '',
            canActivate: [CanActivate],
            loadChildren: () => import('./modules/dashboard/dashboard.module').then(module => module.DashboardModule)
        },
        {
            path: 'companies',
            canActivate: [CanActivate],
            loadChildren: () => import('./modules/company/company.module').then(module => module.CompanyModule)
        }
    ]
},
{
    path: 'login',
    loadChildren: () => import('./modules/login/login.module').then(module => module.LoginModule),
    data: {
        defaultReuseStrategy: true, // Ignore our custom route strategy
        resetReuseStrategy: true // Logout redirect user to login and all data are destroyed
    }
}];

재사용 전략:

export class AppReuseStrategy implements RouteReuseStrategy {

private handles: Map<string, DetachedRouteHandle> = new Map();

// Asks if a snapshot from the current routing can be used for the future routing.
public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
}

// Asks if a snapshot for the current route already has been stored.
// Return true, if handles map contains the right snapshot and the router should re-attach this snapshot to the routing.
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
    if (this.shouldResetReuseStrategy(route)) {
        this.deactivateAllHandles();
        return false;
    }

    if (this.shouldIgnoreReuseStrategy(route)) {
        return false;
    }

    return this.handles.has(this.getKey(route));
}

// Load the snapshot from storage. It's only called, if the shouldAttach-method returned true.
public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
    return this.handles.get(this.getKey(route)) || null;
}

// Asks if the snapshot should be detached from the router.
// That means that the router will no longer handle this snapshot after it has been stored by calling the store-method.
public shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return !this.shouldIgnoreReuseStrategy(route);
}

// After the router has asked by using the shouldDetach-method and it returned true, the store-method is called (not immediately but some time later).
public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void {
    if (!handle) {
        return;
    }

    this.handles.set(this.getKey(route), handle);
}

private shouldResetReuseStrategy(route: ActivatedRouteSnapshot): boolean {
    let snapshot: ActivatedRouteSnapshot = route;

    while (snapshot.children && snapshot.children.length) {
        snapshot = snapshot.children[0];
    }

    return snapshot.data && snapshot.data.resetReuseStrategy;
}

private shouldIgnoreReuseStrategy(route: ActivatedRouteSnapshot): boolean {
    return route.data && route.data.defaultReuseStrategy;
}

private deactivateAllHandles(): void {
    this.handles.forEach((handle: DetachedRouteHandle) => this.destroyComponent(handle));
    this.handles.clear();
}

private destroyComponent(handle: DetachedRouteHandle): void {
    const componentRef: ComponentRef<any> = handle['componentRef'];

    if (componentRef) {
        componentRef.destroy();
    }
}

private getKey(route: ActivatedRouteSnapshot): string {
    return route.pathFromRoot
        .map((snapshot: ActivatedRouteSnapshot) => snapshot.routeConfig ? snapshot.routeConfig.path : '')
        .filter((path: string) => path.length > 0)
        .join('');
    }
}

다음은 work! 레퍼런스:https://www.cnblogs.com/lovesangel/p/7853364.html

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

export class CustomReuseStrategy implements RouteReuseStrategy {

    public static handlers: { [key: string]: DetachedRouteHandle } = {}

    private static waitDelete: string

    public static deleteRouteSnapshot(name: string): void {
        if (CustomReuseStrategy.handlers[name]) {
            delete CustomReuseStrategy.handlers[name];
        } else {
            CustomReuseStrategy.waitDelete = name;
        }
    }
   
    public shouldDetach(route: ActivatedRouteSnapshot): boolean {
        return true;
    }

   
    public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        if (CustomReuseStrategy.waitDelete && CustomReuseStrategy.waitDelete == this.getRouteUrl(route)) {
            // 如果待删除是当前路由则不存储快照
            CustomReuseStrategy.waitDelete = null
            return;
        }
        CustomReuseStrategy.handlers[this.getRouteUrl(route)] = handle
    }

    
    public shouldAttach(route: ActivatedRouteSnapshot): boolean {
        return !!CustomReuseStrategy.handlers[this.getRouteUrl(route)]
    }

    /** 从缓存中获取快照,若无则返回nul */
    public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
        if (!route.routeConfig) {
            return null
        }

        return CustomReuseStrategy.handlers[this.getRouteUrl(route)]
    }

   
    public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return future.routeConfig === curr.routeConfig &&
            JSON.stringify(future.params) === JSON.stringify(curr.params);
    }

    private getRouteUrl(route: ActivatedRouteSnapshot) {
        return route['_routerState'].url.replace(/\//g, '_')
    }
}

커스텀 루트 재사용 전략의 실장에서는, 다음의 문제에 직면했습니다.

  1. 루트 부가/분리 작업: 서브스크립션 관리, 청소 등
  2. 마지막 파라미터화된 경로 상태(메모리 최적화)만 유지합니다.
  3. 상태가 아닌 컴포넌트를 재사용: 상태 관리 도구를 사용하여 상태를 관리합니다.
  4. "다른 경로에서 생성된 ActivatedRouteSnapshot을 재접속할 수 없습니다" 오류입니다.

그래서 나는 이런 문제들을 해결하는 도서관을 썼다.라이브러리는 연결/분리 후크를 위한 서비스와 데코레이터를 제공하며 경로의 경로가 아닌 분리된 경로를 저장하기 위해 경로의 구성요소를 사용합니다.

예:

/* Usage with decorators */
@onAttach()
public onAttach(): void {
  // your code...
}

@onDetach()
public onDetach(): void {
  // your code...
}

/* Usage with a service */
public ngOnInit(): void {
  this.cacheRouteReuse
    .onAttach(HomeComponent) // or any route's component
    .subscribe(component => {
      // your code...
    });

  this.cacheRouteReuse
    .onDetach(HomeComponent) // or any route's component
    .subscribe(component => {
      // your code...
    });
}

라이브러리: https://www.npmjs.com/package/ng-cache-route-reuse

위의 답변은 모두 훌륭하지만 라우터가 저속하고 너무 네스트되어 있는 경우 모두 정상적으로 동작하지 않습니다.

하기 위해 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」shouldReuseRoute해야 합니다.

Path A: abc/xyx/3
Path B: abc/rty/8

<abc>
 <router-outlet></router-outlet>
</abc>

/* If we move from pathA to pathB or vice versa, 
 * then  `routeConfig` will be same since we are routing within the same abc, 
 * but to store the handle properly, you should store fullPath as key.
*/

  shouldReuseRoute(
    future: ActivatedRouteSnapshot,
    curr: ActivatedRouteSnapshot
  ): boolean {
  
    return future.routeConfig === curr.routeConfig;
  }


  private getPathFromRoot(route: ActivatedRouteSnapshot) {
    return (route["_urlSegment"]["segments"] as UrlSegment[])
      .map((seg) => seg.path)
      .join("/");
  }

언급URL : https://stackoverflow.com/questions/41280471/how-to-implement-routereusestrategy-shoulddetach-for-specific-routes-in-angular

반응형