引子
PC端需求,下钻层级较多,且均为列表页。列表进入到新建页面,新建成功则返回上一页并刷新页面,编辑/新建页面直接点击返回则返回上一页页面的缓存状态。
方案
hack手法
this.$router.push({
path: '/product',
query: {
t: +new Date()
}
})
此方法虽然简单,但是有明显缺陷:
- 丑;
- 前进刷新,浏览器回退的上一页url上有时间戳,但是面包屑上点击跳转无时间戳只能刷新,除非维护页面的时间戳。
meta路由depth层级
// router-view页面
<template>
<div id="app">
<keep-alive :include="include">
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
</div>
</template>
// router.js
meta: {
keepAlive: true,
deepth: x // 路由层级1,2,...n
}
监听$route维护include数组,下钻则添加当前页name(此name为.vue里的name)到数组里,回退则去除。
此方法缺陷为:
- 兼容性差,逻辑推演太复杂:同模块跨级跳转;不同下钻的模块间跳转。
- 路由文件需要静态配置一堆depth
Vuex
此方案适用于React框架。将表格查询的Filter信息存储起来,回退时用Filter查询列表,若回退想回到第一页,可跳转前修改Vuex的信息再跳转。
此方案缺陷:
- 不是真缓存,存在接口请求和重新渲染;
- 页面如果有多个接口请求,包含数据看板、多列表,存储变复杂;
- 滚动位置。
keepAlive刷新方案
- 多页面会用到router,但不一定用到vuex,那么方案如下:
-
EventBus;
-
监听$route变化;
-
activated;
- 刷新有两个方案:
- Html标签上添加v-if,改变成false再改回true;
- keepAlive标签上添加include、exclude。利用exclude优先于include,include初始值为router里所有meta为keepAlive的name集合(此name为.vue里的name,router配置的name规范成和.vue保持一致方便初始化),需要刷新时将刷新页面的name值push进exclude数组,渲染完再移除,逻辑比维护include数组简单很多。
第一个方案更通用,当前页刷新也可以使用,可以用在点击侧边栏刷新(当前页没变化不会出发$route变化和activated);第二个方案缺陷是无法刷新当前页,但使用了keepAlive自带方法更优雅。
<keep-alive :include="include" :exclude="exclude" v-if="keepAliveReload">
<router-view></router-view>
</keep-alive>
data() {
return {
keepAliveReload: true,
exclude: []
}
},
created() {
this.keepAliveReloadInit = Object.assign({}, this._data)
if (!this.$eventBus._events['keepAliveReload']) {
this.$eventBus.$on('keepAliveReload', () => {
this.keepAliveReload = false
this.$nextTick(() => {
Object.assign(this, this.keepAliveReloadInit)
})
})
}
},
watch: {
$route: function(to, from) {
if (to.meta.keepAlive && to.meta.reload) {
this.exclude.push(to.name)
this.$nextTick(() => {
this.exclude.pop()
Object.assign(this, this.keepAliveReloadInit)
})
to.meta.reload = false
to.meta.loaded = true
}
}
}
刷新参数携带方案
-
meta元信息;
-
params;
// 方案1
this.$route.meta.reload = true
this.$router.push('/pointExchange/accountDetail')
// router守卫传递reload信息
router.beforeEach((to, from, next) => { to.meta.reload = from.meta.reload from.meta.reload = false })
// 方案2
this.$router.push({ name: 'accountDetail', params: { reload: true } })
方案2有些缺陷:
- 潜在的与路由配置path/:reload冲突;
- 只能使用name跳转params参数才能设置成功。
方案1好处是不限制$router是path还是name跳转,且meta元信息冲突的可能性更低。
总结
最终方案为:
keepAlive缓存,在keepAlive标签上v-if,include/exclude实现刷新,通信为$route监听、EventBus(用来侧边栏刷新,见方案),刷新标识通过meta元信息传递实现。
此方案我认为通用性最高(无需vuex、多页面有router),逻辑简单,实现也不复杂。