复用代码
自己写的angular路由复用,用着基本挺好。
定义的的顺序就是复用的逻辑,离开某路由页面时,
shouldDetach
觉得可复用后,
store
去保存路由,再次进入页面时,
shouldAttach
判断下可通过去缓存中拿到复用的路由后,
retrieve
返回路由缓存,最后
shouldReuseRoute
对获得缓存路由与将进入的路由进行比对,一致通过则使用缓存的路由复现之前的画面。
// simple-reuse-strategy.ts
import {ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy} from '@angular/router'
import Debug from './debug.service'
import {ComponentRef} from '@angular/core'
export class SimpleReuseStrategy implements RouteReuseStrategy {
public static handlers: { [key: string]: DetachedRouteHandle } = {}
private static waitDelete: any
private reuseModules: any = [] // 必须复用的模块,即进入此模块均可复用, 在index-routing 中的路由名
private unReuseArr: any = ['home', 'credit_reports-paylog'] // 无需复用的模块
private storeRoute: any
/**
* @desc 清除所有的路由缓存,还要把路由缓存中的组件销毁
*/
static clearCacheRouters() {
for (const key in this.handlers) {
if (this.handlers[key]) {
this.deactivateOutlet(this.handlers[key])
delete this.handlers[key]
}
}
Debug.log('this.handlers', {...this.handlers})
}
private static deactivateOutlet(handle: DetachedRouteHandle): void {
const componentRef: ComponentRef<any> = handle ? handle['componentRef'] : null
if (componentRef) {
componentRef.destroy()
}
}
/**
* @desc 表示对所有路由允许复用 如果你有路由不想利用可以在这加一些业务逻辑判断
* 顺位,1、明确不需要复用的,即unReuseArr中的,2、明确需要复用的,即reuseModules中的,3、后一个页面路由包含前一个路由
*/
public shouldDetach(route: ActivatedRouteSnapshot): boolean {
if (!route.routeConfig || route.routeConfig.loadChildren) {
return false
}
let _find = false
const len = this.unReuseArr.length
for (let i = 0; i < len; i++) {
if (this.getRouteUrl(route).indexOf(this.unReuseArr[i]) > -1) {
_find = true
break
}
}
if (_find) return !_find // 如果有明确不包含的,则返回false
const pathname = window.location.pathname, routeUrl = route['_routerState'].url
if (this.includeReuseModule(this.getPurePath(pathname))) {
if (routeUrl.includes(pathname) && routeUrl.length > pathname.length) {
// index/metadatas/add -> index/metadatas 不可
return false
}
return true // 只要;, ? 之前的path,包含可复用数组的路由,则可复用
}
return !_find
}
/**
* @desc 当路由离开时会触发。按path作为key存储路由快照&组件当前实例对象
*/
public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
if (!route.routeConfig || route.routeConfig.loadChildren) return
const key = this.getRouteUrl(route)
this.storeRoute = key // 存储上一个路由
SimpleReuseStrategy.handlers[key] = handle
}
/**
* @desc 当路由进入详情的时候才允许还原路由
*/
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (!route.routeConfig || route.routeConfig.loadChildren) return false
return !!SimpleReuseStrategy.handlers[this.getRouteUrl(route)]
}
/**
* @desc 从缓存中获取快照,若无则返回null
*/
public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
if (!route.routeConfig || route.routeConfig.loadChildren) {
return null
}
SimpleReuseStrategy.waitDelete = this.getRouteUrl(route) // 存储当前路由
return SimpleReuseStrategy.handlers[this.getRouteUrl(route)]
}
/**
* @desc 进入路由触发,判断是否同一路由
*/
public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
const include = (future.routeConfig && this.reuseModules.includes(future.routeConfig.path)) ||
(curr.routeConfig && this.reuseModules.includes(curr.routeConfig.path))
return include || (future.routeConfig === curr.routeConfig &&
JSON.stringify(future.params) === JSON.stringify(curr.params))
}
public deleteRouteSnapshot(name: string): void {
if (SimpleReuseStrategy.handlers[name]) {
delete SimpleReuseStrategy.handlers[name]
}
}
deleteAllRouteSnapshot() {
SimpleReuseStrategy.handlers = {}
}
// 某一个链接是否包含可复用模块
includeReuseModule(path) {
let has = false
const len = this.reuseModules.length
for (let i = 0; i < len; i++) {
if (path.indexOf(this.reuseModules[i]) > -1) {
has = true
break
}
}
return has
}
// 如index/employees/detail;edit=1?name="好朋友", get index/employees/detail
getPurePath(link) {
const pathname = link.split('?')[0]
return pathname ? pathname.split(';')[0] : ''
}
//eg: _credit_member-cards_MemberCardsComponent
private getRouteUrl(route: ActivatedRouteSnapshot) {
// return route['_routerState'].url.replace(/\//g, '_')
return route['_routerState'].url.replace(/\//g, '_')
+ '_' + (route.routeConfig.loadChildren || route.routeConfig.component.toString().split('(')[0].split(' ')[1] )
}
}
曾出现的问题:Cannot reattach ActivatedRouteSnapshot created from a different route
解决方法:
- 若未混用懒加载策略 则完善
shouldDetach
方法即可
/**
* @desc 表示对所有路由允许复用 如果你有路由不想利用可以在这加一些业务逻辑判断
*/
public shouldDetach(route: ActivatedRouteSnapshot): boolean {
if (!route.routeConfig || route.routeConfig.loadChildren) {
return false
}
return true
}
- 若混用了懒加载策略,在上面的getRouteUrl方法中,以前是直接使用
route.routeConfig.path
做key去缓存。即
private getRouteUrl(route: ActivatedRouteSnapshot) {
return route['_routerState'].url.replace(/\//g, '_')
}
但path指的是配置在路由(XXXX.routing.ts)中的路由规则,不同模块有相同规则的机率相当大,比如每个模块可能都有 ’ ’
做默认路由,所以会出现错误,解决方法如下,使用path和componentRef的名称组合作为key缓存,基本不会重复。
//eg: _credit_member-cards_MemberCardsComponent
private getRouteUrl(route: ActivatedRouteSnapshot) {
// return route['_routerState'].url.replace(/\//g, '_')
return route['_routerState'].url.replace(/\//g, '_')
+ '_' + (route.routeConfig.loadChildren || route.routeConfig.component.toString().split('(')[0].split(' ')[1] )
参考资料
http://kavil.com.cn/2017/05/16/Angular-RouteReuse/(含component销毁)
https://segmentfault.com/a/1190000009971757(NavigationEnd监听)