利用vue封装TabBer导航栏
1、利用vue-cli2构建项目
需要在构建项目的时候安装vue-router
2、搭建项目文件路径
asset里存放图片和css,同时创建一个img文件夹存放图片,再建立一个tabbar子文件夹存放tabbar需要的图片。分别是导航栏未被选中和被选中的图片。
components里就存放公共的组件,比如轮播、导航栏等。
router里就存放router路由组件了。
views就具体存放页面的组件,比如主页(home)、分类(category)、购物车(shopcart)、我的(profile)。
3、封装TabBarItem
TabBarItem就是TabBar导航栏的单个组件,比如:
所以我们先从最小的封装起。
创建一个组件,名叫TabBarItem.vue。这个组件里面只有一张图片和一排文字(实际上会有两张图片,活跃的时候显示深色图标,不活跃的时候显示浅色图标):
<template><!-- TabBarItem模板 -->
<div class="tab-bar-item">
<!-- 图片 -->
<div class="item-icon">
<!-- 不活跃 -->
<slot v-if="!isActive" name="item-icon">导航栏原始图片</slot>
<!-- 活跃 -->
<slot v-else name="item-icon-active">导航栏活跃图片</slot>
</div>
<!-- 文字(动态绑定class) -->
<div :class="{'item-text-active':isActive}">
<slot name="item-text">导航栏文字</slot>
</div>
</div>
</template>
所以我就利用isActive来判断该页面是否活跃。同时定义好Item的样式:
<style scoped>
/* item样式 */
.tab-bar-item {
/* 依次从左往右顺序排列 */
flex: 1;
/* 文字居中 */
text-align: center;
/* 高度 */
height: 49px;
/* 文字大小 */
font-size: 14px;
}
/* item图片样式 */
.item-icon img{
width: 24px;
height: 24px;
}
/* item文字样式 */
.item-text-active {
color: #13227a;
}
</style>
注意这里我们选择使用slot插槽来占位图片和文字,方便外部直接用图片和文字来装填,不然就需要创建4个Item。
Tips: slot插槽仅用来占位用,只需要name属性,class不会作用在他身上。所以直接利用div来绑定class,然后将slot插槽包围起来就好了。
4、封装TabBar
TabBarItem封装好了就需要将TabBarItem封装到TabBar上了。在一个组件里使用另一个组件就需要先用import导入然后再注册:
<script>
// 导入TabBarItem
import TabBarItem from "./TabBarItem";
export default {
name: "TabBar",
components:{
TabBarItem
}
}
</script>
然后在template中使用4个TabBarItem就好了:
<template>
<div class="tab-bar">
<!--首页-->
<tab-bar-item>
<img src="../../assets/img/tabbar/home.svg" alt="首页" slot="item-icon">
<img src="../../assets/img/tabbar/home_active.svg" alt="首页" slot="item-icon-active">
<div slot="item-text">首页</div>
</tab-bar-item>
<!--分类-->
<tab-bar-item>
<img src="../../assets/img/tabbar/category.svg" alt="分类" slot="item-icon">
<img src="../../assets/img/tabbar/category_active.svg" alt="分类" slot="item-icon-active">
<div slot="item-text">分类</div>
</tab-bar-item>
<!--购物车-->
<tab-bar-item>
<img src="../../assets/img/tabbar/shopcart.svg" alt="购物车" slot="item-icon">
<img src="../../assets/img/tabbar/shopcart_active.svg" alt="购物车" slot="item-icon-active">
<div slot="item-text">购物车</div>
</tab-bar-item>
<!--我的-->
<tab-bar-item>
<img src="../../assets/img/tabbar/profile.svg" alt="我的" slot="item-icon">
<img src="../../assets/img/tabbar/profile_active.svg" alt="我的" slot="item-icon-active">
<div slot="item-text">我的</div>
</tab-bar-item>
</div>
</template>
注意slot分别将对应的图片和文字插入到插槽里。
<style scoped>
.tab-bar{
/* 依次从左往右顺序排列 */
display: flex;
/* 背景颜色 */
background-color: #f6f6f6;
/* 位置固定 */
position: fixed;
/* 位置固定到屏幕最下方 */
left: 0;
right: 0;
bottom: 0;
}
</style>
这是TabBar的样式。
5、将TabBar引入App.vue中
将组件引入另一个组件同样是需要先用import导入再在components里注册
<script>
// 导入TabBar
import TabBar from "./components/tabber/TabBar";
export default {
name: 'App',
components:{
TabBar
}
}
</script>
然后就可以在template中使用了:
<template>
<div id="app">
<!-- 底下导航栏 -->
<tab-bar></tab-bar>
</div>
</template>
到这里,我们大致的TabBar就封装完成。但是我们还需要给它封装跳转的相关逻辑,并且结合vue-router来实现动态路由切换的目的。
6、懒加载4个TabBarItem对应的页面并且配置在routes里
// 导入Vue和VueRouter
import Vue from "vue";
import VueRouter from "vue-router";
// 懒加载组件
const Home = () => import('../views/home/Home');
const Category = () => import('../views/category/Category');
const Shopcart = () => import('../views/shopcart/Shopcart');
const Profile = () => import('../views/profile/Profile');
// 全局使用VueRouter
Vue.use(VueRouter);
// 实例化router
const router=new VueRouter({
routes:[
// 主页
{
path: '/',
redirect: '/home'
},
// Home页
{
path: '/home',
component: Home
},
// Category页
{
path: '/category',
component: Category
},
// Shopcart页
{
path: '/shopcart',
component: Shopcart
},
// Profile页
{
path: '/profile',
component: Profile
}
],
mode: 'history'
});
// 导出router
export default router;
这样就可以在页面里利用router或者router-link来进行跳转了。同时在main.js里引入和注册router:
import 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)
})
7、在TabBarItem里利用$router来跳转页面
因为我们没有使用router-link来跳转页面,所以我们就需要用this.$router.push()
或者this.$router.replace()
来使用代码进行直接跳转。并且我们需要明白一个逻辑就是为了提高代码的复用性,我们肯定是只需要一个TabBarItem,然后在TabBarItem里放置插槽,然后在外部动态的向插槽里放置图片和文字,所以我们就需要在TabBarItem里动态获取需要跳转的路径然后再在TabBarItem里进行跳转。所以我们就需要在父组件,也就是TabBar里给每个TabBarItem绑定path,然后点击该TabBarItem后获得该path然后再进行跳转。所以我们就需要在TabBar里给每个Item加上path:
<template>
<div class="tab-bar">
<!--首页-->
<tab-bar-item path="/home">
<img src="../../assets/img/tabbar/home.svg" alt="首页" slot="item-icon">
<img src="../../assets/img/tabbar/home_active.svg" alt="首页" slot="item-icon-active">
<div slot="item-text">首页</div>
</tab-bar-item>
<!--分类-->
<tab-bar-item path="/category">
<img src="../../assets/img/tabbar/category.svg" alt="分类" slot="item-icon">
<img src="../../assets/img/tabbar/category_active.svg" alt="分类" slot="item-icon-active">
<div slot="item-text">分类</div>
</tab-bar-item>
<!--购物车-->
<tab-bar-item path="/shopcart">
<img src="../../assets/img/tabbar/shopcart.svg" alt="购物车" slot="item-icon">
<img src="../../assets/img/tabbar/shopcart_active.svg" alt="购物车" slot="item-icon-active">
<div slot="item-text">购物车</div>
</tab-bar-item>
<!--我的-->
<tab-bar-item path="/profile">
<img src="../../assets/img/tabbar/profile.svg" alt="我的" slot="item-icon">
<img src="../../assets/img/tabbar/profile_active.svg" alt="我的" slot="item-icon-active">
<div slot="item-text">我的</div>
</tab-bar-item>
</div>
</template>
注意tab-bar-item标签上的path。因为这是父组件的path,所以我们就可以在子组件利用props来获取:
<script>
export default {
name: "TabBarItem",
props: {
// 从父组件获得path
path: {
type: String,
required: true
},
},
methods: {
itemClick() {
this.$router.replace(this.path);
},
},
}
</script>
直接利用props获得path,然后再通过this.$router.replace()
将从props里获得的path绑定然后直接跳转。这样就达到了跳转的目的。但是有一个问题:假如说我处在当前页面再进行跳转的话就会跳转到/home/home
这个路径下,显然,这是不正确的。所以我们要进行判断,如果现在跳转的页面仍然是当前页面,那么就不跳转:
itemClick() {
// 将要跳转页面的path
let nextPath=this.path;
// 当前页面的path
let currentPath=this.$route.path;
// 避免重复点击当前页报错
if (nextPath!==currentPath){
this.$router.replace(nextPath);
}
},
利用this.path
获得在TabBar里点击TabBarItem的路径path,然后再利用this.$route.path
获得当前活跃的路径(也就是当前页面所处的路径)进行比较,如果相同,说明用户仍然想跳转到该页面,此时不执行跳转,否则将执行跳转。
此时,我们就解决了页面跳转问题并且解决了重复点击Item会报错的问题。
8、动态获取页面的活跃情况并且切换样式
在实际开发中,用户一般点击到哪个Item,那个Item就会高亮,和其他的Item有着不同的样式,所以我们需要完成动态切换Item样式的需求。首先,我们需要创建一个computed计算属性来保存item的是否活跃:
computed: {
// 判断当前页面是否处于活跃,true:活跃;false:不活跃
isActive() {
/*
* 判断逻辑是:取当前活跃页面的路径字符对从父组件
* 获得的path进行比较,如果前者包含后者,说明当前
* 页处于活跃。(包含indexOf的返回值就是0,return
* true,不包含返回-1,return false)
* */
return this.$route.path.indexOf(this.path)===0;
}
},
逻辑就是利用从父组件props里获取的path来对this.$route.path(当前页面的路径)进行匹配,如果从父组件传来的路径(this.path)存在当前页面路径(this.$route.path)就返回0,否则返回-1或者字符串存在的下标。所以,就会return,如果等于0说明相匹配,就表明当前路径确实是活跃的,否则不活跃。所以在template里:
<template><!-- TabBarItem模板 -->
<div class="tab-bar-item" @click="itemClick">
<!-- 图片 -->
<div class="item-icon">
<!-- 不活跃 -->
<slot v-if="!isActive" name="item-icon">导航栏原始图片</slot>
<!-- 活跃 -->
<slot v-else name="item-icon-active">导航栏活跃图片</slot>
</div>
<!-- 文字(动态绑定class) -->
<div :class="{'item-text-active':isActive}">
<slot name="item-text">导航栏文字</slot>
</div>
</div>
</template>
利用isActive来判断当前页面是否处于活跃,如果处于活跃,显示导航栏活跃图片(深色),同时绑定上深色字体的样式。
9、在需要的地方加上router-view供内容显示
最后在App.vue里加上router-view以显示内容:
<template>
<div id="app">
<!-- 显示内容 -->
<router-view></router-view>
<!-- 底下导航栏 -->
<tab-bar></tab-bar>
</div>
</template>