Vue+ElementUI实现动态tabs

后台管理系统里,单页面应用往往还不足以满足我们的开发需求。比方说,用户更喜欢以标签页的形式记录打开过的页面。可以通过一些小技巧来实现动态tabs操作的功能。

思路

  1. 设置源数组options,该数组用于动态渲染tabs
  2. 点击左侧导航栏时动态push进options一条数据
  3. 删除tabs时,动态删除options里对应的数据

难点

  1. 点击左侧导航栏时路由需要进行跳转
  2. 切换以及删除最右侧的tab时需要进行路由跳转
  3. 浏览器地址栏手动输入路由时进行跳转
  4. 如何缓存部分tab页

具体步骤

一、创建基于Vuex的js的文件,用来保存打开过的tabs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

// 应用初始状态
const state = {
options: [],
activeIndex: '/'
}

// 定义所需的 mutations
const mutations = {
// 添加tabs
add_tabs (state, data) {
this.state.options.push(data);
},
// 删除tabs
delete_tabs (state, route) {
let index = 0;
for (let option of state.options) {
if (option.route === route) {
break;
}
index++;
}
this.state.options.splice(index, 1);
},
// 设置当前激活的tab
set_active_index (state, index) {
this.state.activeIndex = index;
}
}

// 创建 store 实例
export default new Vuex.Store({
state,
mutations
})

二、首页的html代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!-- 此处放置el-tabs代码 -->
<div class="template-tabs">
<el-tabs
v-model="activeIndex"
type="border-card"
closable
@tab-click="tabClick"
v-if="options.length"
@tab-remove="tabRemove">

<el-tab-pane
:key="item.route"
v-for="(item, index) in options"
:label="item.name"
:name="item.route">

</el-tab-pane>
</el-tabs>
</div>
<transition name="fade" mode="out-in">
<keep-alive>
<router-view v-if="$route.meta.keepAlive">
<!-- 这里是会被缓存的视图组件 -->
</router-view>
</keep-alive>
</transition>
<transition name="fade" mode="out-in">
<router-view v-if="!$route.meta.keepAlive">
<!-- 这里是不被缓存的视图组件 -->
</router-view>
</transition>

三、动态渲染tabs的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
methods: {
// tab切换时,动态的切换路由
tabClick (tab) {
let path = this.activeIndex;
this.$router.replace({path: path});
},
tabRemove (targetName) {
// 首页不可删除
if(targetName == '/') {
return;
}
this.$store.commit('delete_tabs', targetName);
if (this.activeIndex === targetName) {
// 设置当前激活的路由
if (this.options && this.options.length >= 1) {
this.$store.commit('set_active_index', this.options[this.options.length-1].route);
this.$router.replace({path: this.activeIndex});
} else {
this.$router.replace({path: '/'});
}
}
}
},
computed: {
options () {
return this.$store.state.options;
},
activeIndex: {
get () {
return this.$store.state.activeIndex;
},
set (val) {
this.$store.commit('set_active_index', val);
}
}
},
watch: {
'$route'(to) {
let flag = false;
for (let option of this.options ) {
if (option.route === to.path) {
flag = true;
this.$store.commit('set_active_index', to.path);
break
}
}
if (!flag) {
this.$store.commit('add_tabs', {route: to.path, name: to.name});
this.$store.commit('set_active_index', to.path);
}
}
}

四、路由配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let routes = [
{
path: '/',
component: Home,
name: '导航一',
iconCls: 'el-icon-message',//图标样式class
children: [
//{ path: '/main', component: Main, name: '主页', hidden: true },
{ path: '/table', component: Table, name: 'Table', meta: { keepAlive: false} },
{ path: '/form', component: Form, name: 'Form', meta: { keepAlive: true} },
{ path: '/user', component: user, name: '列表' },
]
}
]

总结

keep-alive标签可以满足对组件缓存的需求,但问题是如果不加以限制的话,所有的二级路由都会缓存,容易造成浏览器占用内存过大的问题。所以通过给路由配置文件加上meta属性来判断当前路由是否需要缓存,从而避免内存占用过大的问题。也可以通过添加include特性,来实现动态缓存标签页。更多关于keep-alive的问题,可以参考这篇文章。虽然是SPA单页面应用,但也很好地解决了以前iframe能够实现的功能,确实值得称赞。

参考的项目地址:基于vue+element-ui的后台管理系统动态tabs实践

avatar

chilihotpot

You Are The JavaScript In My HTML