综合练习-实现Tab-Bar

1. 实现Tab-Bar思路
- 下方单独的
Tab-Bar组件如何封装?- 自定义
Tab-Bar组件,在APP中使用 - 让
Tab-Bar位置在底部,并设置你需要的样式
- 自定义
Tab-Bar中显示的内容由外部决定- 定义插槽
- flex布局平分
Tab-Bar
- 自定义
Tab-Bar-Item,可以传入图片和文字- 定义
Tab-Bar-Item,并定义两个插槽:图片和文字 - 给插槽外层包装
div,设置样式 - 填充插槽,实现底部
Tab-Bar的效果
- 定义
- 传入高亮图片
- 定义另一个插槽,插入
active-icon的数据 - 定义一个变量
isActicve,通过v-show来决定是否显示对应的icon
- 定义另一个插槽,插入
Tab-Bar-Item绑定路由数据- 安装路由:
npm install vue-router --save - 在
router/index.js配置路由信息,并创建对应的组件 main.js中注册routerApp.vue中使用router-link和router-view
- 安装路由:
- 点击item跳转到对应的路由,并且动态决定
isActive- 监听
item的点击,通过this.$router.replace()替换路由路径 - 通过
this.$route.path.indexOf(this.link)!==-1来判断是否使active
- 监听
- 动态计算active样式
- 封装新的计算属性:
this.isActive?{'color': 'red'}:{}
- 封装新的计算属性:
2. 代码实现
使用vue init webpack 02-vue-router-tabbar-v1新建一个项目工程(使用vuecli2)。
在文件夹assest下新建css/base.css,用于初始化css
base.css
1
2
3
4body {
padding: 0;
margin: 0;
}修改App.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 id="app">
<div id="tar-bar">
<div class="tar-bar-item">首页</div>
<div class="tar-bar-item">分类</div>
<div class="tar-bar-item">购物车</div>
<div class="tar-bar-item">我的</div>
</div>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
/* style中引用使用@import */
@import url('./assets/css/base.css');
#tar-bar {
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100, 100, 100, .2);
}
.tar-bar-item {
flex: auto;
text-align: center;
height: 49px;
font-size: 20px;
}
</style>使用npm run dev,查看网页效果
思考:如果每次都要复用tabbar,那每次都需要复制粘贴,应该要把tabbar抽离出来,vue就是组件化思想。
将tabbar抽离成组件
在components下新建tabbar文件夹,新建
TarBar.vue和TabBarItem.vue,TabBarItem组件是在组件TarBar中抽取出来的,可以传入图片和文字(比如首页),所有需要使用插槽<slot>代替。TarBar.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 id="tab-bar">
<!-- 插槽代替tabbaritem -->
<slot></slot>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: 'TabBar'
}
</script>
<style scoped>
#tab-bar {
display: flex;
background-color: #f6f6f6;
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 1px rgba(100, 100, 100, .2);
}
</style>TabBar弄一个slot插槽用于插入TabBarItem组件(可能插入多个).
TabBarItem.vue
1 | <template> |
TabBarItem组件中插入2个插槽一个用于插入图片一个用于插入文字。
MainTabBar.vue
1 | <template> |
在MainTabBar组件中加入另外2个组件。
注意此处使用
~assets和@/components是使用了别名配置,详情请看3.别名配置
最后在app.vue中导入MainTabBar组件。
1 | <template> |
效果如图所示,将组件进行了分离重组,只要修改MainTabBar组件就可以修改图片和文字描述,可以复用。

如何实现点击首页首页字体变红图片变红色
这里需要用到路由的
active-class。思路:引用2张图片,一张是正常颜色一张是红色,使用
v-if和v-else来处理是否变色,在路由处于活跃状态的时候,变红色。引入路由使用路由就不细说了,这里仅贴上tabbar的修改代码。
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<template>
<div class="tab-bar-item" :style="activeStyle" @click="itemClick">
<!-- item-icon表示图片插槽 item-text表示文字插槽,例如首页 -->
<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 type="text/ecmascript-6">
export default {
name: 'TabBarItem',
props:{
path:String,
activeColor:{
type:String,
default:'red'
}
},
computed: {
isActive(){
return this.$route.path.indexOf(this.path) !== -1
},
activeStyle(){
return this.isActive ? {color: this.activeColor} : {}
}
},
methods: {
itemClick(){
this.$router.push(this.path)
}
}
}
</script>
<style scoped>
.tab-bar-item {
flex: auto;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
height: 24px;
width: 24px;
margin-top: 3px;
vertical-align: middle;
margin-bottom: 2px;
}
</style>- 使用
props获取传递的值,这里传递是激活颜色,默认是红色 - 设置计算属性
isActive和activeStyle,分别表示激活状态和激活的样式 - 定义
itemClick()方法用于获取点击事件,点击后使用代码实现路由跳转,这里使用默认的hash模式 - 使用
v-if和v-else来进行条件判断
MainTabBar.vue组件
1
2
3
4
5
6
7<TabBarItem path="/home">
<img slot="item-icon" src="/note_image/Vue/vue2/~assets/img/tabbar/home.png" alt="" srcset="">
<img slot="item-icon-active" src="/note_image/Vue/vue2/~assets/img/tabbar/home_active.png" alt="" srcset="">
<template v-slot:item-text>
<div>首页</div>
</template>
</TabBarItem>添加激活状态的图片与未激活的图片并列。
- 使用
配置路由信息,参考之前的代码
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
33import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const routes = [
{
path: '/',
redirect: '/home'//缺省时候重定向到/home
},
{
path: '/home',
component: () => import ('../views/home/Home.vue')
},
{
path: '/categories',
component: () => import ('../views/categories/Categories.vue')
},
{
path: '/shop',
component: () => import ('../views/shop/Shop.vue')
},
{
path: '/profile',
component: () => import ('../views/profile/Profile.vue')
},
]
export default new Router({
routes,
// linkActiveClass:"active"
})
修改main.js和App.vue
1
2
3
4
5
6
7
8
9
10
11
12import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})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 id="app">
<router-view></router-view>
<MainTabBar></MainTabBar>
</div>
</template>
<script>
import MainTabBar from '@/components/MainTabBar'
export default {
name: 'App',
components: {
MainTabBar
}
}
</script>
<style>
/* style中引用使用@import */
@import url('./assets/css/base.css');
</style>
3. 别名配置
经常的我们向引入图片文件等资源的时候使用相对路径,诸如../assets/xxx这样的使用../获取上一层,如果有多个上层就需要../../xxx等等这样不利于维护代码。此时就需要一个能获取到指定目录的资源的就好了。
配置
在webpack.base.config中配置使用别名,找到resolve:{}模块,增加配置信息
1 | resolve: { |
这里@指定目录是src,例如@/components表示src/components目录,assets表示src/assets前缀,如果是assets/img就表示src/assets/img目录。









