如何实现路由重用策略应该分离在角度2的特定路由

我有一个角2模块,其中我已经实现了路由,并希望在导航时存储的状态。
用户应该能够:

  1. 使用「搜寻公式」搜寻文件
  2. 导航到其中一个结果
  3. 导航回“ searchresult”-不与服务器通信

这是可能的,包括 RouteReuseStrategy
问题是:
如何实现不应该存储文档?

因此,路由路径“ document”的状态应该被存储,而路由路径“ document/: id”的状态不应该被存储?

83849 次浏览

安德斯,问得好!

我得到了几乎与您相同的用例,并且想要做同样的事情!User search > get results > User 导航到 result > User 导航回 > 迅速恢复成果,但是您不希望存储用户导航到的特定结果。

博士

您需要有一个实现 RouteReuseStrategy并在 ngModule中提供策略的类。如果要在存储路由时进行修改,请修改 shouldDetach函数。当返回 true时,Angular 存储路由。如果要在附加路由时进行修改,请修改 shouldAttach函数。当 shouldAttach返回 true 时,Angular 将使用存储的路由代替请求的路由。这里有一个 笨蛋,你可以随便玩玩。

关于路由重用策略

通过问这个问题,您已经理解了 RouteReuseStrategy 允许您告诉 Angular 没有销毁一个组件,但实际上是为了将其保存以便以后重新呈现。这很酷,因为它允许:

  • 服务器调用减少
  • 提高速度
  • AND 默认情况下,组件呈现的状态与保留的状态相同

如果您想暂时离开一个页面,即使用户已经在其中输入了 很多文本,那么最后一个选项也很重要。企业应用程序会喜欢这个特性,因为 过度的表单数量!

这就是我想出来的解决问题的办法。正如你所说的,你需要利用在版本3.4.1和更高版本中@angle/router 提供的 RouteReuseStrategy

待命

首先 确保你的项目有@angle/router 3.4.1或更高版本。

接下来 ,创建一个包含实现 RouteReuseStrategy的类的文件。我打电话给我的 reuse-strategy.ts,并把它放在 /app文件夹的安全保管。目前,这个类应该是这样的:

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


export class CustomReuseStrategy implements RouteReuseStrategy {
}

(不要担心你的 TypeScript 错误,我们将解决所有问题)

通过向您的 app.module提供类来完成基础工作 。请注意,您还没有编写 CustomReuseStrategy,但是应该继续,并且 importreuse-strategy.ts都一样。还有 import { RouteReuseStrategy } from '@angular/router';

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

最后一部分 是编写控制路由是否被分离、存储、检索和重新连接的类。在我们进入旧的 复制/粘贴之前,我将在这里做一个简短的力学解释,因为我理解它们。请参考下面的代码了解我所描述的方法,当然,还有大量的文档 在密码里

  1. 导航时,shouldReuseRoute启动。这个方法对我来说有点奇怪,但是如果它返回 true,那么它实际上重用了您当前所在的路由,并且没有触发其他任何方法。如果用户正在导航离开,则返回 false。
  2. 如果 shouldReuseRoute返回 false,则 shouldDetach触发。shouldDetach确定是否要存储路由,并返回一个 boolean,指示是否存储路由。我将通过检查 想要根据 route.routeConfig.path存储的路径数组来执行 这是您应该决定存储/不存储路径的地方,如果数组中不存在 path,则返回 false。
  3. 如果 shouldDetach返回 true,则启动 store,这是一个机会,您可以存储关于路线的任何信息。无论您做什么,您都需要存储 DetachedRouteHandle,因为 Angular 以后用它来识别存储的组件。下面,我将 DetachedRouteHandleActivatedRouteSnapshot都存储到类的本地变量中。

因此,我们已经了解了存储的逻辑,但是如何导航 组件呢?Angular 是如何决定拦截你的导航系统并把存储的导航系统放回原位的呢?

  1. 同样,在 shouldReuseRoute返回 false之后,shouldAttach运行,这是您决定是否要在内存中重新生成或使用组件的机会。如果你想重用一个存储的组件,返回 true,你很好地在你的方式!
  2. 现在 Angular 会问你,“你希望我们使用哪个组件?”,通过从 retrieve返回该组件的 DetachedRouteHandle来指示。

这就是你需要的逻辑!在下面的 reuse-strategy.ts代码中,我还为您留下了一个漂亮的函数,它将比较两个对象。我用它来比较未来路线的 route.paramsroute.queryParams与存储的。如果这些都匹配,我想使用存储的组件,而不是生成一个新的组件。但如何做到这一点是 由你决定!

重用策略

/**
* 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;
}
}

行为

这个实现只在路由器上存储用户访问的每个唯一路由一次。这将继续添加到整个用户在站点上的会话中存储在内存中的组件。如果希望限制存储的路由,那么可以使用 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
}
}

因为角度将 只存储一个实例的路由,这个存储将是轻量级的,我们将只存储位于 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-angle 的默认 路由重用策略实现: https://github.com/NativeScript/nativescript-angular/blob/cb4fd3a/nativescript-angular/router/ns-route-reuse-strategy.ts

不要被接受的答案吓倒,这是相当直截了当的。这里有一个快速的答案,你需要什么。我建议你至少读一下这个被接受的答案,因为里面有很多细节。

这个解决方案不像已接受的答案那样进行任何参数比较,但是它可以很好地存储一组路由。

App.module.ts 导入:

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


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

共享/路由:

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;
}
}

要将 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},
]}

更多信息 给你

参考文献: 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, '_')
}
}

除了已被接受的答案(由 Corbfon 提供)和 Chris Fremgen 简短而直接的解释之外,我还想添加一种更加灵活的处理路由的方法,这种方法应该使用重用策略。

两个答案都将要缓存的路由存储在数组中,然后检查当前路由路径是否在数组中。这个检查是用 shouldDetach方法完成的。

我发现这种方法不够灵活,因为如果我们想要更改路由的名称,我们需要记住在 CustomReuseStrategy类中也要更改路由的名称。我们可能忘记更改它,或者我们团队中的其他开发人员可能决定更改路由名称,甚至不知道 RouteReuseStrategy的存在。

我们可以使用 data对象直接在 RouterModule中标记路由,而不是将要缓存的路由存储在数组中。这样,即使我们更改了路由名称,仍然可以应用重用策略。

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

然后在 shouldDetach方法中,我们利用了这一点。

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

另一个更有效、完整和可重用的实现。这个模块支持延迟加载模块@U ur Dinç 和集成@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 版本略有改进: 循环遍历基对象和比较对象的属性。请记住,您可以使用一个外部的、更可靠的实现,比如 lodashisEqual方法。

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;
}
}

如果你有一个最好的方法来生成独特的键注释我的答案,我会更新代码。

感谢所有分享他们解决方案的人。

在我们的案例中,所有提到的解决方案都不够充分:

  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('');
}
}

我在实现自定义路由重用策略时遇到了这些问题:

  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("/");
}


角13(28/02/2022版本)

在阅读了大量的指导和建议之后,我可以解释一下:

首先,你必须了解什么是未来和过去。

当你从 localhost/a导航到 localhost/b,现在你在 b。

案例1: 你想从 /a -> /b开始

  • Should dReuseRoute: false因为 future !== current
  • Should dDetach: true,因为我们将来会把(detach)任何东西保存到 store并等待重用(attach)。
  • 应该检索: true || fase检查 handler如果是我们 attach保存的未来组件重用。如果没有,我们什么也不做。(在这种情况下是否定的)

案例2: 你想从 /b?id=1 -> /b?id=2开始

  • 应该重用路由: true,因为 future === current;
  • 跳过
  • 应该检索: 跳过

案例3: 你想从 /b -> /a回去

  • Should dReuseRoute: false因为 future !== current
  • Should dDetach: true,因为我们将来会把(detach)任何东西保存到 store并等待重用(attach)。
  • 应该检索: true || fase检查 handler如果是我们 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 enter image description here

最后输入角度队的正确代码: 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;
}
}