VUE + ELEMENTUI,多标签页共存的实现

这个主题,早在一年前就已经创建,也写了一些内容,碍于在应用上体验始终不够完美,一直只存着草稿。

经过多个平台实践,多次迭代,一些功能加了又减了,最后还是回归了最精简的版本,已适用于大部分的场景,若有需要,可自行扩展。


关键逻辑

  • 使用keep-alive来缓存各标签页
  • 通过vue-routerbeforeEach方法来更新标签信息
  • 通过vuex来保存标签信息
  • 通过vuex来使关闭页不被缓存

核心代码

  1. 定义vuex的跨页变量(store/index.js)

     import Vuex from 'vuex'
     Vue.use(Vuex)
     export default new Vuex.Store({
       state: {
         worktab: {
           list: [
             {
               name: 'my',
               tabname: '主页',
               path: '/page/my'
             }
           ],
           current: {
             name: 'my',
             tabname: '主页',
             path: '/page/my'
           }
         },
         closingPage: ''
       },
       mutations: {
         worktabRemove (state, p) {
           // 关闭标签
           let ind = state.worktab.list.findIndex(s => s.name === p)
           if (ind > -1) {
             // 清理 keep alive - start
             state.closingPage = state.worktab.list[ind].name
             // 清理 keep alive - end
             state.worktab.list.splice(ind, 1)
           }
           if (p === state.worktab.current.name) {
             // 是当前页,返回上一页
             router.back()
           }
         },
         worktabRoute (state, p) {
           let ind = state.worktab.list.findIndex(s => s.name === p.to.name)
           if (ind > -1) {
             // 标签已存在
             state.worktab.current = state.worktab.list[ind]
           } else {
             // 标签不存在,现在新建
             state.worktab.list.push(p.to)
             state.worktab.current = p.to
           }
           state.closingPage = ''
         }
       },
       actions: {
         worktabRemove ({commit}, p) {
           commit('worktabRemove', p)
         },
         worktabRoute ({commit}, p) {
           commit('worktabRoute', p)
         }
       },
       strict: debug
     })
    
  2. 定义worktab标签栏组件,在主容器引用

     <template>
       <div class="cp-worktab">
         <el-tabs v-model="activeTab" type="card" @tab-remove="removeTab" @tab-click="clickTab">
           <el-tab-pane
             v-for="t in worktabs"
             :key="t.name"
             :label="t.tabname"
             :name="t.name"
             :closable="t.name !== 'my'"
           >
           </el-tab-pane>
         </el-tabs>
       </div>
     </template>
     <script>
     export default {
       created () {
         // 进来不是主页时等list加载后再更新一次current
         setTimeout(() => {
           this.activeTab = this.$store.state.worktab.current.name
         }, 500)
       },
       watch: {
         '$store.state.worktab.current' (tab) {
           this.activeTab = tab.name
         }
       },
       computed: {
         worktabs () {
           return this.$store.state.worktab.list
         }
       },
       data () {
         return {
           activeTab: 'name'
         }
       },
       methods: {
         clickTab (tab) {
           this.$router.push(this.worktabs[1 * tab.index].path)
         },
         removeTab (name) {
           this.$store.dispatch('worktabRemove', name)
         }
       }
     }
     </script>
    
  3. 路由控制通过beforeEach来更新标签信息

     import Vue from 'vue'
     import VueRouter from 'vue-router'
     import store from '@/store'
     import Page from '../components/console/Page.vue'
     import My from '../components/console/My.vue'
     Vue.use(VueRouter)
     // 关联路由与组件
     const routes = [
       {
         name: 'root',
         path: '/'
       },
       {
         path: '/page',
         component: Page,
         children: [
           {
             name: 'my',
             path: 'my',
             component: My,
             meta: {
               tabname: '个人主页'
             }
           }
         ]
       }
     ]
     // 创建路由器
     const router = new VueRouter({
       routes
     })
     router.beforeEach((to, from, next) => {
       next()
       store.dispatch('worktabRoute', {
         to: {
           name: to.name ? to.name : '',
           tabname: (to.meta && to.meta.tabname) ? to.meta.tabname : '',
           path: to.path
         },
         from: {
           name: from.name ? from.name : '',
           tabname: (from.meta && from.meta.tabname) ? from.meta.tabname : '',
           path: from.path
         }
       })
       return
     })
     export default router
    
  4. 主容器通过closingPage变量来及时清理关闭页面的缓存

     <template>
       <div>
         <cp-worktab></cp-worktab>
         <div class="cp-content">
           <keep-alive :exclude="closingPage">
             <router-view></router-view>
           </keep-alive>
         </div>
       </div>
     </template>
     <script>
     import Worktab from '../module/Worktab'
     export default {
       components: {
         cpWorktab: Worktab
       },
       data () {
         return {}
       },
       computed: {
         closingPage () {
           return this.$store.state.closingPage
         }
       }
     }
     </script>
    
若您觉得我的博文对您有帮助,欢迎点击下方按钮对我打赏
打赏