0%

【Vue】前端路由小实例:Tabbar

前端路由实例练手:Tabber功能实现

一、基础框架实现

1.1 目录结构

1.2 代码内容

1
2
3
4
5
/* base.css */
body {
padding: 0;
margin: 0;
}
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
<!-- App.vue -->
<template>
<div id="app">
<div id="tab-bar">
<div class="tab-bar-item">首页</div>
<div class="tab-bar-item">分类</div>
<div class="tab-bar-item">购物车</div>
<div class="tab-bar-item">我的</div>
</div>
</div>
</template>

<script>
export default {
name: 'App'
}
</script>

<style>
/* 引用样式 */
@import "./assets/css/base.css";

#tab-bar {
display: flex; /* tab-bar水平分步 */
background: #f6f6f6;

/* tabbar放置底部 */
position: fixed;
left: 0;
right: 0;
bottom: 0;

/* 设置阴影过度:水平位移 垂直位移 阴影宽度 颜色 */
box-shadow: 0 -3px 1px rgba(100, 100, 100 .1) ;
}

/* tabbar均等分,且文字居中 */
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
}
</style>

1.3 效果图


二、对TabBar进行封装

2.1 目录结构

2.2 代码内容

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
<!-- 封装tabbar -->
<template>
<div id="tab-bar">
<!-- <div class="tab-bar-item">-->
<!-- <div>图片1</div>-->
<!-- 首页-->
<!-- </div>-->
<!-- <div class="tab-bar-item">-->
<!-- <div>图片2</div>-->
<!-- 分类-->
<!-- </div>-->
<!-- <div class="tab-bar-item">-->
<!-- <div>图片3</div>-->
<!-- 购物车-->
<!-- </div>-->
<!-- <div class="tab-bar-item">-->
<!-- <div>图片4</div>-->
<!-- 我的-->
<!-- </div>-->
<slot></slot> <!--使用插槽分离tabbar-item内容-->
</div>
</template>

<script>
export default {
name: "TabBar"
}
</script>

<style scoped>
#tab-bar {
display: flex; /* tab-bar水平分步 */
background: #f6f6f6;

/* tabbar放置底部 */
position: fixed;
left: 0;
right: 0;
bottom: 0;

/* 设置阴影过度:水平位移 垂直位移 阴影宽度 颜色 */
box-shadow: 0 -3px 1px rgba(100, 100, 100 .1);
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- 封装tabbar-item -->
<template>
<div class="tab-bar-item">
<!-- <div>图片</div>-->
<!-- <div>动态内容</div>-->
<slot name="item-icon"></slot> <!--为了不写死内容,动态使用插槽-->
<slot name="item-text"></slot>
</div>
</template>

<script>
export default {
name: "TabBarItem"
}
</script>

<style scoped>
/* tabbar均等分,且文字居中 */
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
}
</style>
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
<!-- 重新修改App.vue内容 -->
<template>
<div id="app">
<TabBar>
<TabBarItem>
<div slot="item-icon">图片1</div>
<div slot="item-text">首页</div>
</TabBarItem>
<TabBarItem>
<div slot="item-icon">图片2</div>
<div slot="item-text">分类</div>
</TabBarItem>
<TabBarItem>
<div slot="item-icon">图片3</div>
<div slot="item-text">购物车</div>
</TabBarItem>
<TabBarItem>
<div slot="item-icon">图片4</div>
<div slot="item-text">我的</div>
</TabBarItem>
</TabBar>
</div>
</template>

<script>
//导入组件
import TabBar from "./components/tabbar/TabBar";
import TabBarItem from "./components/tabbar/TabBarItem";

export default {
name: 'App',
//组件注册
components: {
TabBar,
TabBarItem
}
}
</script>

<style>
/* 引用样式 */
@import "./assets/css/base.css";
</style>

2.3 效果图


三、页面激活状态

3.1 目录结构

3.2 代码内容

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
<!-- 修改: TabBarItem.vue -->
<template>
<div class="tab-bar-item">
<div v-if="!isActive"> <!--判断激活状态使用对应的图片-->
<slot name="item-icon"></slot>
</div>
<div v-else>
<slot name="item-icon-active"></slot>
</div>
<div :class="{active: isActive}">
<slot name="item-text"></slot>
</div>
</div>
</template>

<script>
export default {
name: "TabBarItem",
data() {
return {
isActive: false //判断是否激活
}
}
}
</script>

<style scoped>
/* tabbar均等分,且文字居中 */
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
}

/* 激活状态文字颜色 */
.active {
color: pink;
}
</style>
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
<!-- 修改: App.vue -->
<template>
<div id="app">
<TabBar>
<TabBarItem>
<div slot="item-icon">图片1</div>
<div slot="item-icon-active" style="color: pink">图片1-1</div> <!--添加激活状态的图片-->
<div slot="item-text">首页</div>
</TabBarItem>
<TabBarItem>
<div slot="item-icon">图片2</div>
<div slot="item-icon-active" style="color: pink">图片2-1</div>
<div slot="item-text">分类</div>
</TabBarItem>
<TabBarItem>
<div slot="item-icon">图片3</div>
<div slot="item-icon-active" style="color: pink">图片3-1</div>
<div slot="item-text">购物车</div>
</TabBarItem>
<TabBarItem>
<div slot="item-icon">图片4</div>
<div slot="item-icon-active" style="color: pink">图片4-1</div>
<div slot="item-text">我的</div>
</TabBarItem>
</TabBar>
</div>
</template>

3.3 效果图


四、结合vue-router前端路由

4.1 目录结构

4.2 代码内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- Home、Categories、ShoppingCart、Profile.vue -->
<template>
<h2>首页</h2>
</template>

<script>
export default {
name: "Home"
}
</script>

<style scoped>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- 修改TabBarItem.vue -->
<template>
<div class="tab-bar-item" @click="itemClick()"> <!--监听点击-->
...省略
</div>
</template>

<script>
export default {
...省略
props: {
path: String //从父组件获取要跳转的路径
},
methods: {
itemClick() {
this.$router.push(this.path).catch(err => err); //页面跳转,并解决重复点击报错
}
}
}
</script>

<style scoped>
...省略
</style>
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
<!-- 修改App.vue文件 -->
<template>
<div id="app">
<router-view></router-view> <!--接收路由跳转内容-->
<TabBar>
<TabBarItem path="/home"> <!--向子组件传递要跳转的地址-->
...
</TabBarItem>
<TabBarItem path="/categories">
...
</TabBarItem>
<TabBarItem path="/shoppingCart">
...
</TabBarItem>
<TabBarItem path="/profile">
...
</TabBarItem>
</TabBar>
</div>
</template>

<script>
...省略
</script>

<style>
...省略
</style>

4.3 效果图


五、激活状态颜色控制

5.1 目录结构

  • 没有变化,并没创建新文件

5.2 代码内容

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
<!--修改:TabBarItem.vue-->
<template>
<div class="tab-bar-item" @click="itemClick()">
...
<div :style="activeStyle"> <!--动态绑定样式-->
<slot name="item-text"></slot>
</div>
</div>
</template>

<script>
export default {
props: {
path: String,
activeColor: { //让他人使用可以自定义颜色,而不用接触vue底层源码
type: String,
default: 'pink'
}
},
// data() {
// return {
// isActive: false
// }
// }, 用计算属性来代替isActive
computed: {
isActive() {
//return this.$route.path.indexOf(this.path) !== -1;
return this.$route.path === this.path; //跟当前活跃路由和组件中路由进行对比,相同就是激活状态
},
activeStyle() {
return this.isActive ? {color: this.activeColor} : {}; //三元表达式决定样式
}
}
}
</script>

<style scoped>
...
/*.active {*/
/* color: pink;*/
/*} 不再需要激活样式了,动态决定激活样式 */
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div id="app">
<router-view></router-view>
<TabBar>
<TabBarItem path="/home" activeColor="blue"> <!--可以传递activeColor来决定激活字体的颜色,实现完全封装,不需要其他人接触vue的源码-->
<div slot="item-icon">图片1</div>
<div slot="item-icon-active" style="color: pink">图片1-1</div>
<div slot="item-text">首页</div>
</TabBarItem>
...
</TabBar>
</div>
</template>

<script>
...
</script>

<style>
...
</style>

5.3 效果图


六、第二次封装

6.1 目录结构

6.2 代码内容

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
<!-- MainTabBar.vue -->
<template>
<TabBar>
<TabBarItem path="/home">
<div slot="item-icon">图片1</div>
<div slot="item-icon-active" style="color: pink">图片1-1</div>
<div slot="item-text">首页</div>
</TabBarItem>
<TabBarItem path="/categories">
<div slot="item-icon">图片2</div>
<div slot="item-icon-active" style="color: pink">图片2-1</div>
<div slot="item-text">分类</div>
</TabBarItem>
<TabBarItem path="/shoppingCart">
<div slot="item-icon">图片3</div>
<div slot="item-icon-active" style="color: pink">图片3-1</div>
<div slot="item-text">购物车</div>
</TabBarItem>
<TabBarItem path="/profile">
<div slot="item-icon">图片4</div>
<div slot="item-icon-active" style="color: pink">图片4-1</div>
<div slot="item-text">我的</div>
</TabBarItem>
</TabBar>
</template>

<script>
import TabBar from "./TabBar"; //记得修改路径
import TabBarItem from "./TabBarItem";

export default {
name: "MainTabBar",
components: {
TabBar,
TabBarItem
}
}
</script>

<style scoped>

</style>
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
<!-- 修改App.vue -->
<template>
<div id="app">
<router-view></router-view>
<MainTabBar></MainTabBar> <!-- 进一步抽取,是App.vue整洁 -->
</div>
</template>

<script>
//导入组件
import MainTabBar from "./components/tabbar/MainTabBar";

export default {
name: 'App',
//组件注册
components: {
MainTabBar
}
}
</script>

<style>
/* 引用样式 */
@import "./assets/css/base.css";
</style>