前言:
因为前期一直在做低码平台,里面有编辑页面跟预览页面,最近新加的需求是,这俩页面是动态路由,需要做缓存处理,并且集成到微前端主应用。
注意:阅读此文章前,需要对keep-alive原理跟微前端有一定的了解哦!
keep-alive源码解析
js
<keep-alive :include="cachedViews">
<router-view :key="key" />
</keep-alive>
include数组,数组里面包裹的是组件name,(它是一个抽象组件,打开vue调试工具你没法看到这个组件的显示。建议集成组件做个扩展,将它改成非抽象组件,如下图,你可以看到keep-alive内部参数)只有name在这个数组中的组件才会被缓存,不然会把对应的已缓存的组件实例销毁。key是组件缓存用来保存缓存组件实例用的key值,当你点击tag时,会根据对应的key取出对应的组件实例进行显示(具体看源码吧,或者别的解析文章,这个原理一定要懂,不然后面的内容可能不太容易理解)
js
// keep-alive缓存源码
// 地址:https://github.com/vuejs/vue/blob/main/src/core/components/keep-alive.ts#L69
cacheVNode() {
const { cache, keys, vnodeToCache, keyToCache } = this
if (vnodeToCache) {
const { tag, componentInstance, componentOptions } = vnodeToCache
cache[keyToCache] = {
name: _getComponentName(componentOptions),
tag,
componentInstance
}
keys.push(keyToCache)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
this.vnodeToCache = null
}
}
},
问题:关闭tag会导致动态路由页面缓存失效
由此可见,动态路由keep-alive是可以缓存的,你传入的key为路由的path就行,但是呢,我们因为做了多页签处理,所以就碰到一个很难受的问题,keep-alive的include的数组保存的组件name,动态路由只是path不一样,但是name是一样的,所以当你关闭一个tag时,会删除页签数组中的对应的组件的name,而这个数组是传入到keep-alive的include的数组,那这样会把name相同但是path不同的组件全都销毁了,缓存就会失效。
微前端多页签缓存方案解析(简洁版)
现在的主应用布局组件Layout结构如下图:
- 解释一下:layout组件是主应用使用的布局组件,tag-view是多页签组件,AppMain是包裹子应用的主内容区组件。目前layout组件为了实现缓存页面,在 app-main 主内容区有keep-alive 组件,要在多层路由中都能缓存则需要多层 keep-alive 组件(注意:子主应用各自有自己的一套路由,比如编辑页面,主应用编辑页路由name是Edit,子应用编辑页路由的name也是Edit,它俩的path要一致),所以子应用也有对应的keep-alive包裹。
- 当主应用删除一个tag时,会删除对应的组件name,并且会将删除的组件的path通知到子应用,子应用拿到path找到对应组件的name,将缓存的include中的name删除。
- 问题来了,动态路由name是一样的呀,上面有说到,就是你打开多个编辑页,你只要删了其中一个tag,那就会触发多次组件销毁,并通知多次到子应用,子组件对应的也是会去删除include中的name,也会导致缓存直接失效了。
解决方案
主应用:
- 继承keep-alive组件,创建KeepAliveManuallyCache组件,设置组件为非抽象组件,设置ref,并在内部封装一个函数clearCacheByKey(参数是route的path),这个函数的作用是用于手动删除缓存
- tag关闭时,抛出一个删除事件到layout组件,监听这个方法,删除事件触发时,拿到KeepAliveManuallyCache的ref,并调用clearCacheByKey,销毁对应path的组件实例(clearCacheByKey的实现参考keep-alive源码中的pruneCacheEntry的方法)
- 设置一个标志,在使用KeepAliveManuallyCache组件进行显示时,不要去删除对应的缓存中的组件name(删了缓存就失效了,理由上面有说)
- 在原来使用keep-alive的地方,使用component 组件渲染,暴露keepAliveName字段,让用户自主传入keep-alive的名称,进行渲染
子应用:
- 因为keep-alive是根据include的值name去决定组件缓存的,不在include中的组件,会被销毁,所以子组件更简单一点的解决方案是,动态添加路由,在点击编辑进行跳转之前将编辑页组件的name重写,生成动态的name的组件,比如Edit-${id},这样在关闭tag的时候,由于每个编辑页的name是唯一的,所以不会把所有的编辑页的实例给销毁
- 预览页同上
- 其实主应用也可以采用1的方案,但是主应用有很多子应用页面,不可能每个都那么处理的
这样问题就解决了,嘿嘿嘿!!!
最后,感谢大家阅读我的小文章,请帮我点亮一下我的小心心吧!!!