angular 路由组件缓存复用(类似于vue的keep-alive指令)

更新日期: 2022-11-22阅读: 229标签: 路由

类似于vue的keep-alive指令一样,在路由切换的时候,不用销毁和重新实例化组件。

angular提供了路由策略来实现对组件的缓存和复用。我们使用的多级嵌套路由在切换的时,父级路由出口的实例不会重新实例化。就是angular内部使用默认的路由复用策略实现的,这点在看完下面的流程分析就明白了。


一、概念

1、路由树

我们知道,在配置了路由导航的angular应用会形成一棵应用的路由树


应用会从根开始逐级去匹配每一级的路由节点和routeConfig,并检测实例化路由组件,其中routeConfig涵盖树里的每一个节点,包括懒加载路由


2、路由复用策略

RouteReuseStrategy是angular提供的一个路由复用策略,暴露了简单的接口

abstract  class  RouteReuseStrategy {
  // 确定是否应重用路由
  abstract  shouldReuseRoute(future:  ActivatedRouteSnapshot, curr:  ActivatedRouteSnapshot): boolean
  // 存储分离的路由 存储“null”值应删除以前存储的值
  abstract  store(route:  ActivatedRouteSnapshot, handle:  DetachedRouteHandle):  void
  // 确定是否应重用路由
  abstract  shouldAttach(route:  ActivatedRouteSnapshot): boolean
  // 检索以前存储的路由
  abstract  retrieve(route:  ActivatedRouteSnapshot):  DetachedRouteHandle  |  null
  // 确定是否应分离此路由(及其子树)以供以后重用。若 `true` 会触发 `store
  // 离开的路由,是否储存
  abstract  shouldDetach(route:  ActivatedRouteSnapshot): boolean
}


二、方法解析

1、shouldReuseRoute

检测是否复用路由,该方法根据返回值来决定是否继续调用,如果返回值为true则表示当前节点层级路由复用,将继续下一路由节点调用,入参为的future和curr不确定,每次都交叉传入;否则,则停止调用,表示从这个节点开始将不再复用。
两个路由切换的时候是从“路由树”的根开始从上往下层级依次比较和调用的,并且两边每次比较的都是同一层级的路由节点配置。root路由节点调用一次,非root路由节点调用两次这个方法,第一次比较父级节点,第二次比较当前节点。

还是以上面的路由树为例,它的检测层级是这样的:


对比图示,方法的每一次调用时比较的都是同一层级的路由配置节点,就是像图中被横线穿在一起的那些一样,即入参的future和curr是同级的。

举个例子,shouldReuseRoute方法的常见实现为:

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

这时当路由从“main/cop/web/pc”切换到“main/cop/fan/list/group”的调用顺序是这样的:

1
root --> main --> web / fan (返回false)

即到第3层的时候routeConfig不一样,返回false,调用结束,得到不复用的“分叉路由点”

这个方法得到的结果很重要,将作为其他好几个方法的基础

2、retrieve

紧接着shouldReuseRoute方法返回false的节点调用,入参route即是当前层级路由不需要复用。以上个例子说明,此时的route是main/cop/fan/的路由节点。 retrieve调用根据返回结果来决定是否继续调用:如果返回的是null,当前路由对应的组件会实例化,并继续对其子级路由调用retrieve方法,直到遇到缓存路由或到末级路由

在本次路由还原时也会调用,用来获取缓存示例

3、shouldDetach

用来判断刚刚离开的上一个路由是否复用,其调用的时机也是当前层级路由不需要复用,shouldReuseRoute方法返回false的时候。以上个例子说明,首次调用的入参route是main/cop/web/的路由节点。 shouldDetach方法根据返回结果来决定是否继续调用:如果返回的是false,则继续下一层级调用该方法,当前路由对应的组件会实例化,并继续对其子级路由调用retrieve方法,直到返回true或者是最末级路由后才结束。

4、store

紧接着shouldDetach方法返回true的时候调用,存储需要被缓存的那一级路由的DetachedRouteHandle;若没有返回true的则不调用。 以上个例子说明,若我们设置了main/cop/web/pc的keep=true,此时的入参route是main/cop/web/pc节点,存储的是它的实例对象。

无论路径上有几个可以被缓存的路由节点,被存储的只有有一个,就是Detach第一次返回true的那次 在本次路由还原后也会调用一次此方法存储实例

5、shouldAttach

判断是否允许还原路由对象及其子对象,调用时机是当前层级路由不需要复用的时候,即shouldReuseRoute()返回false的时候,而且,并不是所有的路由层级都是有组件实例的,只有包含component的route才会触发shouldAttach。 如果反回false,将继续到当前路由的下一带有component的路由层级调用shouldAttach,直到返回true或者是最末级路由后才结束。 当shouldAttach返回true时就调用一次retrieve方法和store方法


三、调用顺序

shouldReuseRoute -> retrieve -> shouldDetach -> store -> shouldAttach -
-> retrieve(若shouldAttach返回true) -> store(若shouldAttach返回true) 



四、应用

export class RouteMsg {
constructor(public type: string, public url: string, public route: ActivatedRouteSnapshot) { }
}

export class AppReuseStrategy implements RouteReuseStrategy {

private static routeText$ = new Subject<RouteMsg>()
private static handlers: Map<string, DetachedRouteHandle> = new Map()
public static routeReuseEvent = AppReuseStrategy.routeText$.asObservable()
/**
* 确定是否应分离此路由(及其子树)以供以后重用。若 `true` 会触发 `store
* @param route
* @returns
*/
shouldDetach(route: ActivatedRouteSnapshot): boolean {
if (this.hasInValidRoute(route)) {
return false
}
AppReuseStrategy.routeText$.next(new RouteMsg('detach', this.getUrl(route), route))
return Boolean(route.data.keep)
}
/**
* 存储分离的路线 存储“null”值应删除以前存储的值
* @param route
* @param handle
*/
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
AppReuseStrategy.handlers.set(this.getUrl(route), handle)
}
/**
* 确定是否应重新附着此路由(及其子树)
* @param route
* @returns
*/
shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (this.hasInValidRoute(route)) {
return false
}
AppReuseStrategy.routeText$.next(new RouteMsg('attach', this.getUrl(route), route))
return AppReuseStrategy.handlers.has(this.getUrl(route))
}
/**
* 检索以前存储的路由
* @param route
* @returns
*/
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
if (this.hasInValidRoute(route)) return null
return AppReuseStrategy.handlers.get(this.getUrl(route))||null
}
/**
* 确定是否应重用路由
* @param future
* @param curr
* @returns
*/
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
let ret = future.routeConfig === curr.routeConfig
if (!ret) return false

const path = ((future.routeConfig && future.routeConfig.path) || '') as string
if (path.length > 0 && ~path.indexOf(':')) {
ret = this.getUrl(future) === this.getUrl(curr)
}
return ret
}

hasInValidRoute(route: ActivatedRouteSnapshot): boolean {
return !route.routeConfig || !!route.routeConfig.loadChildren || !!route.routeConfig.children;
}
getTruthRoute(route: ActivatedRouteSnapshot): ActivatedRouteSnapshot {
let next = route;
while (next.firstChild) next = next.firstChild;
return next;
}
/**
* 根据快照获取URL地址
*/
getUrl(route: ActivatedRouteSnapshot): string {
let next = this.getTruthRoute(route);
const segments: string[] = [];
while (next) {
segments.push(next.url.join('/'));
next = next.parent!;
}
const url = `/${segments
.filter(i => i)
.reverse()
.join('/')}`;
return url;
}
}

//在对应组件订阅该对象
//此时组件不再重新初始化,以前放在Init和Destroy钩子里做的事情可能需要考虑找个时机来做,可以使rxjs订阅来做,修改策略代码,增加subject,
AppReuseStrategy.routeReuseEvent.pipe(
takeUntil(this.unsubscribe$)
).subscribe(res => {
if (res.type == 'detach') {

} else if (this.router.url.includes(res.url)) {
if (res.type == 'attach') {

}
}
})
}
作者:liuk123
来源:http://www.cicode.cn/blog/detail/412

链接: https://www.fly63.com/article/detial/12572

vue路由history模式_如何去除vue项目中的#

在使用vue-cli搭建的环境中,浏览器上URL地址中是存在#的,这是由于vue-router 默认 hash 模式,不难发现#的出现真的很丑陋。官网给出了如何使用history模式mode: history

vue路由传参主要的3种方式

vue中路由传参主要的3种方式:query方式(push时使用path来匹配)、params模式(push时使用name来匹配)、location预声明参数模式(push使用path来匹配,但是它跟params模式不同)

vue动态加载路由_实现vue动态加载路由器设置

我们的通用的后台管理系统中,我们会根据权限的粗细不同,会对每个角色每个权限每个资源进行控制。同样的我们也需要实现一个这样的功能。 这篇文章我将主要讲vue端的实现,关于后台接口我就不会涉及,当我接触的时候我们的后台接口是springcloud实现。

两种前端路由的实现方式

前后端分离开发模式,后端会把路由控制丢在前端,这几天再开发单页面小的项目,手动撸了个路由。前端路由实现有两种方法。HTML5 History API包括2个方法:history.pushState()和history.replaceState(),和1个事件:window.onpopstate。hash + location.onhashchange

vue动态路由_vue-router通过接口请求动态生成路由的实现

在后台管理系统中,一般都会采用权限管理。路由菜单数据都会保存到数据库中,在vue-router 2.2版本新增了一个router.addRoutes(routes)方法,即可用它来实现动态路由了

HTML5 History 模式

vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

vue router 路由鉴权(非动态路由)

原本想用动态路由的思路去做,按权限加载对应路由表,但是由于权限可以交叉(比如一个人可以同时是主题管理员和数据服务管理员),导致权限路由表还是得去做判断组合。于是放弃了这个思路,索性就在beforeEach里直接判断了。

vue中路由按需加载的几种方式

使用vue-cli构建项目后,我们会在Router文件夹下面的index.js里面引入相关的路由组件,webpack在打包的时候会把整个路由打包成一个js文件,如果页面一多,会导致这个文件非常大,加载缓慢

vue-router 中参数传递(params,query)

query和params的区别,query相当于get请求,在页面跳转的时候,可以在地址栏看到请求参数,然而params则相当于post请求,参数不会在地址栏中显示。

Node.js的路由

当服务端接收到HTTP请求时,可以通过onRequest() 获取到url, pathname,query,及paramParams参数;为了解析这些数据需要使用url和querystring模块

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!