keepAlive 单页应用多标签 路由缓存的设计

本文介绍了在一个Vue.js应用中如何实现多标签页的缓存管理,包括能够配置部分路由启用或禁用缓存,切换标签页时使用缓存,关闭标签页时清除相应缓存,以及通过导航菜单或页面按钮打开的路由不使用缓存。文章详细阐述了在`store/index.js`中定义全局变量和操作方法,以及在`App.vue`中监听路由变化以处理缓存的实现过程。同时,还展示了在路由配置中如何标记路由以进行缓存。
摘要由CSDN通过智能技术生成

由于使用了多标签显示方式,缓存的设计需求分析

1、设计需求

  • 可以配置一部分路由需要缓存,一部分不需要缓存

  • 在已打开的标签页栏中,切换标签页,使用缓存,不要重新渲染组件

  • 关闭已打开的标签页时,同时清除对应标签页组件缓存

  • 通过点击导航菜单或者页面按钮,打开的路由标签不要使用缓存,需要重新渲染或刷新已打开的标签页

2、代码实现

2.1、store/index.js 定义全局变量 keepAliveList 和 操作方法

增加一个全局变量 keepAliveList ,用来存储需要缓存的组件name,默认值为 ['home']

keepAliveList:['home'],

增加对 变量 keepAliveList 的值更新方法

// 添加缓存路由组件
        ADD_KEEP_ALIVE(state, route){
            if (state.keepAliveList.includes(route.name)) {
                return
            }
            state.keepAliveList.push(route.name)
            console.log('add 缓存' + route.name)
        },
        // 移除缓存路由组件
        DEL_KEEP_ALIVE(state, route){
            const index = state.keepAliveList.indexOf(route.name)
            if(index > -1){
                state.keepAliveList.splice(index, 1)
                console.log('del 缓存' + route.name)
            }
        },
        // 清除缓存路由组件
        CLEAR_KEEP_ALIVE(state){
            state.keepAliveList = ['home']
        },

2.2、App.vue 中操作更新 keepAliveList

在添加标签或点击导航、页面按钮触发路由跳转时,watch $route 里执行方法 addTab , 此时需要添加缓存,如果已存在要跳转的路由缓存,则要刷新缓存

<template>
    <div>
        <div v-if="showApp == true">
​
            <!-- 头部 -->
            <sy-Head @on-refrash="reload"></sy-Head>
​
            <!-- 页标签显示栏 -->
            <sy-Click :titles="history_list" @removeAll="removeAll" @removeTag="removeTab" @changeTag="changeTag"></sy-Click>
​
            <!-- 导航 -->
            <sy-Nav @showPage="showPage"></sy-Nav>
​
            <!-- 页面主体内容 -->
            <keep-alive :include="cachedViews">
                <router-view v-if="$route.meta.keepAlive" :key="key"></router-view>
            </keep-alive>
            <router-view v-if="!$route.meta.keepAlive"></router-view>
​
        </div>
​
        ...
        ...
        
        
    </div>
</template>
​
<script>
    import syHead from './pages/syHead.vue'
    import syNav from './pages/syNav.vue'
    import home from './pages/home.vue'
    import syClick from './pages/syClick.vue'
    var _self;
​
    export default {
        name: 'app',
        components: {
            home,
            syNav,
            syHead,
            syClick
        },
        data() {
            return {
​
                // 用来控制应用加载时页面延迟显示
                showApp: false,
​
                routers: [],
                history_list: [{
                    "title": "系统首页",
                    "path": "/home",
                    "curr": true,
                    "name": "home",
                    "flag": "",
                    _route: 'home', // 用来存储对应的路由只读实例对象
                    "route": {
                        "name": "home",
                        "meta": {
                            "title": "系统首页",
                            "requireAuth": false,
                            "showHeader": true,
                            "showNav": true,
                            "showtag": true,
                            "showPosition": true,
                            "showClick": true
                        },
                        "path": "/home",
                        "query": {},
                        "params": {},
                        "fullPath": "/home"
                    }
                }], // 历史标签
​
                // 标签点击标识,用来控制切换路由时的缓存处理方式,如果是标签点击切换的路由,直接用缓存不需要处理,并且也不需要执行新增标签方法,
                // 如果不是标签点击的,那么要判断当前缓存中是否已存在将要跳转的路由,如果存在要先清除缓存
                isTabClick: false,
                // 键盘control是否被按下
                isUpKey_Control: false,
            }
        },
        created() {
            _self = this;
        },
        mounted: function() {
            // 得到浏览器内框高度
            window.onresize = function() {
                setTimeout(() => {
                    _self.$store.commit('getCss');
                }, 300)
            }
            window.onresize();
​
            /**************** 下面两个键盘监控是用来控制F5刷新组件,Control+F5刷新浏览器  ************/
            // 监控键盘按下事件
            document.onkeydown = function(e) {
                let keyNum = window.event ? e.keyCode : e.which; //获取被按下的键值 
                // 键盘按下control键时
                if (keyNum == 17) {
                    _self.isUpKey_Control = true;
                }
                // 键盘按下F5键时
                if (keyNum == 116) {
                    if (_self.isUpKey_Control == false) {
                        // 刷新当前路由组件
                        _self.reload()
                        // 阻止浏览器刷新
                        return false
                    }
                }
            }
            // 监控键盘按键抬起事件
            document.onkeyup = function(e) {
                let keycode = window.event ? e.keyCode : e.which;
                // 键盘按下control键抬起后
                if (keycode == 17) {
                    _self.isUpKey_Control = false;
                }
            }
​
            // 延迟1秒显示
            setTimeout(() => {
                this.showApp = true
            }, 1000);
        },
​
        methods: {
​
            // 重置缓存:先删除在添加
            resetKeepAive(route) {
                this.deleteKeepAive(route);
                this.$nextTick(() => {
                    //添加缓存路由
                    this.addKeepAive(route);
                    console.log('重置缓存')
                })
            },
​
            // 删除缓存
            deleteKeepAive(route) {
                this.$store.commit('DEL_KEEP_ALIVE', route);
            },
​
            // 添加缓存
            addKeepAive(route) {
                if(route.meta.keepAlive){
                    this.$store.commit('ADD_KEEP_ALIVE', route);
                }
            },
​
            // 导航菜单点击
            showPage: function(router) {
                this.isTabClick = false;
                this.$router.push(router);
            },
​
            // 切换标签
            changeTag(index) {
                this.isTabClick = true;
                for (let i = 0; i < this.history_list.length; i++) {
                    this.history_list[i].curr = false;
                }
                this.history_list[index].curr = true;
                this.$router.push(this.history_list[index]._route);
            },
            
​
            // 不是标签点击的 (isTabClick == false)
            addTab(to) {
                // 判断是否要显示tab标签
                if (!to.meta || !to.meta.showtag) {
                    return
                }
                let haved = false;
​
                // 查找要转向的路由是否在已打开的标签列表里
                for (let i = 0; i < this.history_list.length; i++) {
                    this.history_list[i].curr = false;
​
                    // 如果当前转向的路由,已经在标签列表中,则直接设置对应的标签焦点状态 (如果路由有参数的,需要判断关键参数flag)
                    if (this.history_list[i].title == to.meta.title &&
                        (!to.query || !to.query.flag || this.history_list[i].flag == to.query.flag)) {
                        
                        haved = true;
                        this.history_list[i].curr = true;
​
                        // 如果当前点击的路由已经在缓存列表中,则先清除缓存列表,再添加;
                        this.resetKeepAive(to)
                        break;
                    }
                }
​
                if (!haved) {
                    this.history_list.push({
                        title: to.meta.title,
                        path: to.path,
                        curr: true,
                        name: to.name,
                        flag: (to.query && to.query.flag) ? to.query.flag : "",
                        _route: to,
                        route: {
                            "name": to.name,
                            "meta": to.meta || {},
                            "path": to.name || '',
                            "query": to.query || {},
                            "params": to.params || {},
                            "fullPath": to.fullPath || ""
                        }
                    })
                    this.addKeepAive(to)
                }
            },
​
            // 关闭标签-清除标签页组件缓存(清除缓存时是根据组件name来清除的,所以组件name相同的多个标签页缓存会同时被清除)
            removeTab(index) {
                this.isTabClick = true;
                let name = this.history_list[index].name;
                // 清除缓存
                this.deleteKeepAive(this.history_list[index]);
                
                // 如果关闭的是当前活动标签页
                if (this.history_list[index].curr) {
                    this.history_list.splice(index, 1)
                    if (this.history_list.length == 1) {
                        this.$router.push('/home');
                    } else {
                        this.$router.push(this.history_list[index - 1]._route);
                    }
                } else {
                    this.history_list.splice(index, 1);
                }
            },
​
            // 关闭所有标签
            removeAll() {
                for (let i = this.history_list.length - 1; i > 0; i--) {
                    this.history_list.splice(i, 1);
                }
                this.$store.commit('CLEAR_KEEP_ALIVE');
                this.$router.push('/home');
            },
​
            // 刷新当前标签页组件
            reload() {
​
                let _time = new Date().getTime()
                let index = this.curr_index;
                
                // 先清除当前的路由缓存
                this.deleteKeepAive(this.history_list[index]);
​
                // 路由查询参数中将_VERSION时间戳参数值刷新
                let query = this.history_list[index].route.query || {};
                if (query._VERSION) {
                    this.history_list[index].route.query._VERSION = _time;
                } else {
                    this.$set(this.history_list[index].route.query, '_VERSION', _time);
                }
                // 将参数数组转成地址参数字符串
                let queryString = '';
                this.$XEUtils.each(query, function(value, key) {
                    queryString += key + '=' + value + '&'
                })
                queryString = queryString.substring(0, queryString.length - 1);
                
                // 因为
                let fullPath = this.history_list[index].route.fullPath || '';
                this.history_list[index].route.fullPath = this.history_list[index].path + "?" + queryString
                this.$router.push(this.history_list[index].path + "?" + queryString)
            }
​
        },
        computed: {
            // 当前焦点状态的标签页序号
            curr_index() {
                let index = -1;
                for (let i = 0; i < this.history_list.length; i++) {
                    if (this.history_list[i].curr == true) {
                        index = i;
                        break;
                    }
                }
                return index >= 0 ? index : 0;
            },
            cachedViews() {
                return this.$store.state.keepAliveList
            },
            key() {
                return this.history_list[this.curr_index].route.fullPath
            },
        },
        watch: {
            // 监听路由变化,自动新增标签
            '$route': {
                handler(to, from) {
​
                    // 如果不是标签头点击跳转的路由,要新增标签头
                    if (this.isTabClick == false) {
                        this.addTab(to);
                    }
                    if (!to.query._VERSION) {
                        to.query._VERSION = new Date().getTime()
                    }
                    this.isTabClick = false
                    //this.posMenu(to.path);
                },
                immediate: true
            }
        }
    }
</script>

3.3、配置路由器 routes 的自定义属性

这里以 router/g_om/index.js 为例,如果路由需要被缓存,则配置属性 keepAlive = true

const routes = [
    {
        path:"/g_om_order_list",
        component: g_om_order_list,
        name: "g_om_order_list",
        meta: {
            title:'订单管理',
            requireAuth: false,//需要登录显示
            showHeader: true ,//头部显示
            showNav:true,//菜单显示
            showtag:true,//标签显示
            keepAlive: true, // 需要缓存
        },
    },
    {
        path:"/g_om_order_detail",
        component: g_om_order_detail,
        name: "g_om_order_detail",
        meta: {
            title:'订单详情',
            requireAuth: false,//需要登录显示
            showHeader: true ,//头部显示
            showNav:false,//菜单显示
            showtag:true,//标签显示
            keepAlive: true, // 需要缓存
        },
    },
]
​
​
export default routes;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值