基于CLI学完后所做的第一个较大的vue案例

0.思路

  1. 在App.vue中搭建大致的底部栏样式
  2. 拆解底部栏(大框+切换选项)
  3. 创建路由
  4. 实现动态切换路由(v-on)
  5. 实现动态切换样式(v-bind、props、computed、$route、$router)
  6. 整合底部栏并抽离出一个完整的底部栏组件,并在app.vue导入该完整的底部栏组件

1.结构搭建

首先,就是在app.vue里面将大致结构搭建出来

template代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="tab-bar">
<div class="tab-item">
<img src="../../assets/image/tabbar/home.svg" alt="" slot="image">
<img src="../../assets/image/tabbar/home_active.svg" alt="" slot="img-active">
<div slot="item">首页</div>
</div>
<div class="tab-item">
<img src="../../assets/image/tabbar/home.svg" alt="" slot="image">
<img src="../../assets/image/tabbar/home_active.svg" alt="" slot="img-active">
<div slot="item">首页</div>
</div>
<div class="tab-item">
<img src="../../assets/image/tabbar/home.svg" alt="" slot="image">
<img src="../../assets/image/tabbar/home_active.svg" alt="" slot="img-active">
<div slot="item">首页</div>
</div>
<div class="tab-item">
<img src="../../assets/image/tabbar/home.svg" alt="" slot="image">
<img src="../../assets/image/tabbar/home_active.svg" alt="" slot="img-active">
<div slot="item">首页</div>
</div>
</div>

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
<style>
.tab-bar {
display: flex;
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #f6f6f6;
}

.tab-item {
flex: 1;
height: 49px;
text-align: center;
}

.tab-item img {
width: 24px;
height: 24px;
margin-top: 3px;
vertical-align: middle;
margin-bottom: 2px;
}
</style>

2.根据代码进行模块拆解

在components文件夹下新建一个Tabbar.vue文件

将外部框,也就是包裹四个选项按钮的框进行抽离

代码如下

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
<template>
<div class="tab-bar">
<tab-bar-item>
<img slot="img" src="../../assets/image/tabbar/home.svg" alt="">
<div slot="item">首页</div>
</tab-bar-item>
<tab-bar-item>
<img slot="img" src="../../assets/image/tabbar/category.svg" alt="">
<div slot="item">分类</div>
</tab-bar-item>
<tab-bar-item>
<img slot="img" src="../../assets/image/tabbar/shopcart.svg" alt="">
<div slot="item">购物车</div>
</tab-bar-item>
<tab-bar-item>
<img slot="img" src="../../assets/image/tabbar/profile.svg" alt="">
<div slot="item">我的</div>
</tab-bar-item>
</div>
</template>

<script>
import TabbarItem from "./TabbarItem.vue"
export default {
name: "Tabbar",
components: {
TabbarItem
}
}
</script>

<style scoped>
.tabbar {
display: flex;
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #f6f6f6;
}
</style>

在该文件夹下再新建TabbarItem.vue文件并进行每个子选项(按钮)的代码抽离

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
<template>
<div class="tab-item">
<slot name="image"></slot>
<slot name="item"></slot>
</div>
</template>

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

<style scoped>
.tab-item {
flex: 1;
height: 49px;
text-align: center;
}
.tab-item img {
width: 24px;
height: 24px;
margin-top: 3px;
vertical-align: middle;
margin-bottom: 2px;
}
</style>

3.创建路由,构建相关路由页面

路由代码

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
//导入模块
import Vue from 'vue'
import VueRouter from 'vue-router'

const Home = () => import('../views/Home/Home.vue')
const Category = () => import('../views/Category/Category.vue')
const Profile = () => import('../views/Profile/Proile.vue')
const Shopcart = () => import('../views/Shopcart/Shopcart.vue')


//使用路由
Vue.use(VueRouter)

//路由映射表
const routes = [
{
path: '',
// name: 'home',
redirect: '/home',
},
{
path: '/home',
name: 'home',
component: Home
},
{
path: '/category',
name: 'category',
component: Category
},
{
path: '/shopcart',
name: 'shopcart',
component: Shopcart
},
{
path: '/profile',
name: 'profile',
component: Profile
}]

//新建路由
const router = new VueRouter({
routes,
mode: 'history' //将hash改为history,防止url有#出现

})

//导出路由
export default router

相关路由页面目录

image-20201018191045712

4.动态切换路由

跳回到TabbarItem.vue文件,为其动态绑定一个点击事件change

1
2
3
<div class="tab-item" @click="change">

</div>

实现具体的路由跳转操作

1
2
3
4
5
6
7
8
9
10
<script>
export default {
name: "TabbarItem",
methods: {
change() {
this.$router.push(this.path)
}
}
}
</script>

到这一步,你会发现是不能完成路由跳转的,因为我们需要动态获取到用户点击的是哪个按钮,我们需要动态的获取父级的按钮选项中要跳转的页面,所以我们到它的父级页面去定义一个path属性,同时赋上各自的路由地址

Tabbar.vue代码(path=”地址”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div class="tab-bar">
<tab-bar-item path="/home" >
<img slot="img" src="../../assets/image/tabbar/home.svg" alt="">
<img slot="img-active" src="../../assets/image/tabbar/home_active.svg" alt="">
<div slot="item">首页</div>
</tab-bar-item>
<tab-bar-item path="/category">
<img slot="img" src="../../assets/image/tabbar/category.svg" alt="">
<img slot="img-active" src="../../assets/image/tabbar/category_active.svg" alt="">
<div slot="item">分类</div>
</tab-bar-item>
<tab-bar-item path="/shopcart">
<img slot="img" src="../../assets/image/tabbar/shopcart.svg" alt="">
<img slot="img-active" src="../../assets/image/tabbar/shopcart_active.svg" alt="">
<div slot="item">购物车</div>
</tab-bar-item>
<tab-bar-item path="/profile">
<img slot="img" src="../../assets/image/tabbar/profile.svg" alt="">
<img slot="img-active" src="../../assets/image/tabbar/profile_active.svg" alt="">
<div slot="item">我的</div>
</tab-bar-item>
</div>
</template>

TabbarItem定义props属性去接收这些变量

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
export default {
name: 'TabBarItem',
props:{
path: String,
},
methods: {
change() {
this.$router.push(this.path)
}
}
}
</script>

5.切换选项样式改变

接着,为了实现点击哪个选项,哪个选项的图片以及字体颜色就会相应的改变,我们需要给TabbarItem一个判断语句,用来判断该组件对应的路由当前是否处于活跃状态,首先为图片定义if-else语句,对其进行判断,若活跃,则显示有颜色的图片,同时,定义一个计算属性-isActive,进行语句判断,若当前活跃的页面就是该组件路由,则显示活跃的图片样式—核心代码( this.$route.path.indexOf(this.path) !== -1

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
<template>
<div class="tab-item" @click="change">
<div v-if="!isActive">
<slot name="img"></slot>
</div>
<div v-else>
<slot name="img-active"></slot>
</div>
<slot name='item'></slot>
</div>
</template>

<script>
export default {
name: 'TabBarItem',
props:{
path: String,
},
methods: {
change() {
this.$router.push(this.path)
}
}
computed: {
isActive() {
return this.$route.path.indexOf(this.path) !== -1
}
}
}
</script>

<style scoped>
.tab-item {
flex: 1;
height: 49px;
text-align: center;
}
.tab-item img {
width: 24px;
height: 24px;
margin-top: 3px;
/* 去除图片底部内容 */
vertical-align: middle;
margin-bottom: 2px;
}
/* .isred {
color: red
} */
</style>

然后,动态绑定style,同样使用isActive计算属性进行判断

为字体动态绑定样式

1
2
3
<div :style="activeStyle">
<slot name='item'></slot>
</div>

方法具体实现

1
2
3
4
5
computed: {
activeStyle() {
return this.isActive ? {color: red} :{}
}
}

如果想为其自定义颜色,可以为其定义一个可以动态获取它父组件的属性

1
2
3
4
5
6
7
8
props:{
path: String,
//自定义颜色
activeColor: {
type: String,
default: 'red'
}
}

然后你就可以自定义你想要的颜色啦

在其父组件下自定义颜色

1
2
3
4
5
<tab-bar-item path="/home" activeColor="blue" >
<img slot="img" src="../../assets/image/tabbar/home.svg" alt="">
<img slot="img-active" src="../../assets/image/tabbar/home_active.svg" alt="">
<div slot="item">首页</div>
</tab-bar-item>

6.组件抽离,插槽安排

然后就是将Tabbar.vue进行抽离,定义一个插槽供插入的选项按钮,然后再新建一个MainTabbar文件用来组装这些组件,最后再在App.vue中引用该组件

最后的代码

Tabbar.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div class="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'TarBar'
}
</script>
<style scoped>
.tab-bar {
display: flex;

position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #C7C7C7;
box-shadow: 0, -1px, 1px, rgba(216, 214, 214, 0.1)
}

</style>

TabbarItem.vue

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
53
54
55
56
57
58
59
<template>
<div class="tab-item" @click="change">
<div v-if="!isActive">
<slot name="img"></slot>
</div>
<div v-else>
<slot name="img-active"></slot>
</div>
<div :style="activeStyle">
<slot name='item'></slot>
</div>
</div>
</template>

<script>
export default {
name: 'TabBarItem',
props:{
path: String,
activeColor: {
type: String,
default: 'red'
}
},
methods: {
change() {
this.$router.push(this.path)
}
},
data() {
return {
}
},
computed: {
isActive() {
return this.$route.path.indexOf(this.path) !== -1
},
activeStyle() {
return this.isActive ? {color: this.activeColor} :{}
}
}
}
</script>

<style scoped>
.tab-item {
flex: 1;
height: 49px;
text-align: center;
}
.tab-item img {
width: 24px;
height: 24px;
margin-top: 3px;
/* 去除图片底部内容 */
vertical-align: middle;
margin-bottom: 2px;
}
</style>

MainTabbar.vue

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
<template>
<div>
<tab-bar>
<tab-bar-item path="/home" >
<img slot="img" src="../../assets/image/tabbar/home.svg" alt="">
<img slot="img-active" src="../../assets/image/tabbar/home_active.svg" alt="">
<div slot="item">首页</div>
</tab-bar-item>
<tab-bar-item path="/category">
<img slot="img" src="../../assets/image/tabbar/category.svg" alt="">
<img slot="img-active" src="../../assets/image/tabbar/category_active.svg" alt="">
<div slot="item">分类</div>
</tab-bar-item>
<tab-bar-item path="/shopcart">
<img slot="img" src="../../assets/image/tabbar/shopcart.svg" alt="">
<img slot="img-active" src="../../assets/image/tabbar/shopcart_active.svg" alt="">
<div slot="item">购物车</div>
</tab-bar-item>
<tab-bar-item path="/profile">
<img slot="img" src="../../assets/image/tabbar/profile.svg" alt="">
<img slot="img-active" src="../../assets/image/tabbar/profile_active.svg" alt="">
<div slot="item">我的</div>
</tab-bar-item>
</tab-bar>
</div>
</template>

<script>
import TabBar from "../tabbar/TabBar.vue"
import TabBarItem from "../tabbar/TabBarItem.vue"
export default {
name: 'MainTabBar',
components: {
TabBar,
TabBarItem
}
}
</script>

<style scoped>

</style>

App.vue

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>
<main-tab-bar></main-tab-bar>
</div>
</template>

<script>
import MainTabBar from "./components/maintabbar/MainTabBar.vue"
export default {
name: 'App',
components: {
MainTabBar
}
}
</script>

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