该文章是对《撸vue/cli 3.+ 的正确姿势(快捷导航/keep-alive缓存router-view)》文章的简化,所以不包括如何管理打开的页面的路由列表数据等,以下简化了实现思想,建议使用以下方法实现缓存后,先用假数据试试是否实现了缓存,再考虑结合项目管理打开和关闭每个标签。
一、建立文件实现缓存功能
components 目录下新建 tagPageNav目录,新建config.js、index.js 、keepAliveView.vue 三个文件
config.js如下:作用是配置常量前缀
const config = {
componentsPrefix: "tag-"
};
export default config;
(1)index.js 如下:作用是根据“前缀+路由别名”注册组件, 需要在main.js 引入执行保证跳转前注册成功
import Vue from "vue";
import router from "@/router/index";
import config from "./config";
// 收集所有路由的router.name
const allRouterName = [];
const getName = arr => {
arr.forEach(item => {
if (item.name) {
allRouterName.push(item.name);
}
if (item.children && item.children.length > 0) {
getName(item.children);
}
});
};
getName(router);
// 特殊前缀名加router.name,当做组件名称,使用router-view做承载对应每个页面显示的组件
// 目的实现每个页面重新注册了一个全局组件,并且这个组件是拥有组件名的,对应组件是目标页面组件的。
allRouterName.forEach(i => {
Vue.component(config.componentsPrefix + i, {
render(c) {
return c('router-view')
}
});
});
main.js引入:
import Vue from 'vue'
import App from './App'
.....
import "./components/tagPageNav/index"
(2)keepAliveView.vue如下:替换原来<router-view/>,让page拥有缓存功能。
<template>
<div style="width:100%; height:100%">
<keep-alive :include="include" :exclude="exclude">
<component :is="componentsPrefix + $route.name"> </component>
</keep-alive>
</div>
</template>
<script>
import config from "./config";
export default {
name: "keepAliveView",
data() {
return {
componentsPrefix: config.componentsPrefix
};
},
computed: {
include() {
return []; // 省略,可以先把上面收集的allRouteName或者手写一部分放这里,在页面验证是否缓存了。
},
exclude() {
return []; // 省略
}
}
};
</script>
引入组件keepAliveView,替换<router-view/>
<div>
<!-- layout布局中,page的主题内容区域 -->
<!-- <router-view/> -->
<keepAliveView/>
</div>
以上我们已经能实现多级路由嵌套,并且实现了缓存,但是我们需要自己实现 include返回的数组。建议使用路由拦截+全局状态管理,动态的增加删除一个打开的路由列表的数据。思路参考第二条。
二、刷新逻辑
keepAliveView.vue我们可以根据业务需要计算已经打开的列表,建议放在全局状态里管理, 比如我么有A、B 、C、页面,我们想要都缓存起来那么keepAliveView.vue组件的计算结果应该如下:(假定ABC就是他们的路由别名name)
...
include() {
return [A、B、C];
},
exclude() {
return [];
}
如果我们在此刻刷新B页面:在跳转路由之前(跳转前、跳转前、跳转前),也就是需要路由拦截前判断去往的那个B页面是否需要刷新,提前将它排除在include里,放在exclude里。这样跳转过去才是刷新的。
...
include() {
return [A、C];
},
exclude() {
return [B];
}
跳转到B以后,我们仍然需要将B放入缓存页面,为了目的就是继续缓存,为下一次做准备。
比如我又从B->A,再从A->B时我们想要B保留了之前数据。
所以刷新过程(排除缓存过程)只是路由跳转前到路由页面渲染完毕这一个阶段。
路由刷新完后以后数据任然要加入缓存:
...
include() {
return [A、B、C];
},
exclude() {
return [];
}
**我们的业务逻辑简化以后就是,只有tag标签点击需要缓存,其它任何地方跳转都不缓存,所以我们只需要在 tag标签点击跳转做文章就行。其它跳转依旧。
大致的思路
1、Vuex 需要维护的变量。
fromTag: false, 开关变量。 当前触发访问路由是否来自于tag,
Tag: tag列表, 维护一个数组[ { title, needInclude,routeName}]。
include: 需要缓存的routerName列表, 根据tag里 needInclude:true 的对象, 做成一个getters
2、tag.vue 组件
从vuex里获取tag列表,循环出来
当某个tag点击, 修改 vuex 里 fromTag 变量为true。做跳转。
3、全局路由拦截:
router.beforRouter(()=>{
let name = 目标路由name;
if(store.state.fromTag){
来自于tag点击, 修改 vuex 里 fromTag 变量为false。不做任何操作
}else{
if(vuex的tab列表没有该路由数据){
组装一条tag,塞进 vuex里的tag里
}else{
需要刷新,也就是说,将tag里这条数据needInclude 属性改为false,
vuex 里 include 数组应该会少一条。
这时候 keep-alive 下的include也会排除对应的包裹组件,这样就把缓存清了。
}
}
})
router. afterEach(()=>{
这时候可以无论来自哪儿,找到当前路由name,
根据name在tag中找到对应的那一条数据, needInclude 改为true,为下次缓存做准备。
})
——————————着急写代码的可以走了——————————
三、弊端
不可避免的组件重复渲染,比如新闻列表页和新闻详细页面,有可能它们有新闻模块独立的layout布局。
会有新闻模块公用的头部或者侧栏,但是我们的按照以上说的方法,其实这个单独模块的公用部分是每次都渲染的,我们其实是把一个完整的树形结构的布局,变成了单一一条线的布局。
不懂路由嵌套和layout概念,请看《撸vue/cli 3.+ 的正确姿势(layout的互相嵌套,路由分组)》 地址:https://www.jianshu.com/p/5c871bd581f2
如果你实在有强迫症,你也可以这样去做:把新闻模块当做一个页面看待。
1、我们按照以上方法收集路由name的时候,我们可以有选择的收集,在新闻模块路由做个特殊标记,把新闻看做一个整体,比如新闻列表、详细、新增在循环注册全局组件时,统一都用一个叫做"<tag-news-module>"的组件注册一次即可。
2、必须保证我们的路由无论访问 新闻、详细、新增、keepAliveView.vue 渲染的都是“<tag-news-module>”,也就保证了新闻模块的layout公用部分不重复切换渲染。当然你要考虑include 和 exclude数组对新闻的特殊处理。
...
<keep-alive :include="include" :exclude="exclude">
<component is="tag-news-module"> </component>
</keep-alive>
...
3、这时候要想保证新闻详细和新增在新闻布局里缓存就相对容易了,直接在新闻布局承载内容的<router-view />上加普通的 <keep-alive> 即可,也不需要管理include 和 exclude。
四:总结
以上逻辑可能会比较绕,但是可以避免每个组件加name,后端管理系统一般都是很少一个模块有特殊的 layout布局,所以这种方法是一种一劳永逸的方法,它虽有弊端,但是可忽略不计,相反大大提高了网页新能,只要你理解实现方法,你会觉得这个方法其实类似插件一样,它不会让我们主结构看起来混乱,一如既往的去写我们的代码,而不用在开发某个模块时候去关心缓存如何做。
网友评论