一个vue底部导航栏组件

写一个底部导航栏独立组件,便于以后项目中的使用

源码地址:https://github.com/michaelxuzhi/tabbar

分为创建和封装两步

需求:独立、图标可改、名称可改、flex布局

①分析:首先是TabBar

在这里插入图片描述

TabBar是导航栏的主框架,包裹整个导航栏

创建tabbar -> Tabbar.vue

<template>
    <div id="tab-bar">
        // 这里啥都没有
    </div>
</template>

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

<style scoped>
    #tab-bar {
     
     
        display: flex; /* 让后续所有的item都以flex摆放*//
        background-color: #f6f6f6;
	
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;

        box-shadow: 0 -3px 10px rgba(100, 100, 100, .2);
    }
</style>

创建主导航栏区域 MainTabBar.vue,这里其实是将导航栏的区域Tabbar.vue做成一个独立组件,让它在MainTabBar中展示,因为后续还要在导航栏区域内实现TabBarItem,所以全都做了抽离和独立。

MainTabBar.vue

<template>
    <tab-bar></tab-bar>
</template>

<script>
  import TabBar from "./tabbar/Tabbar";
 
  export default {
     
     
    name: "MainTabBar",
    components:{
     
     
      TabBar
    }
  }
</script>

<style scoped>

</style>

再在App.vue中引用MainTabBar组件

<template>
  <div id="app">
    <main-tab-bar></main-tab-bar>
  </div>
</template>

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

到这里为止,是创建了一个底部导航栏区域,但是里面没有内容,所以Tabbar.vue的高度是0的,也就看不到任何效果,只要给Tabbar.vue的样式加上一点高度,就可以知道现在导航栏区域的样子了,后续会去除这个写死的高度,由TabBarItem的内容来撑开导航栏

height: 50px;

在这里插入图片描述

②接着创建TabBarItem,也就是每一个选项

这些选项都是要插进Tabbar.vue中的,所以Tabbar.vue要设置插槽,让每个Item能添加进去

<slot></slot>

在这里插入图片描述

tabbar -> TabbarItem.vue

在这里插入图片描述

导航栏中已经设置 display:flex;这里item就设置flex:1; item的高度给到49px,这是通常导航栏的高度,有高度就可以撑开导航栏了,还要设置item中图标的样式

<template>
    <div class="tab-bar-item" @click="itemClick">
    <!-- 这里要放图标和文字,会在后面继续新增 -->
    </div>
</template>

<script>
    export default {
     
     
      name: "TabbarItem",
      
      data(){
     
     
        return{
     
     },
      
      methods:{
     
     
          itemClick(){
     
     
              // 点击item会执行的操作
          }
      }
    }
</script>

<style scoped>
    .tab-bar-item {
     
     
        flex: 1;
        text-align: center;
        height: 49px;

        font-size: 14px;
    }

    .tab-bar-item img {
     
     
        margin-top: 3px;
        width: 24px;
        height: 24px;
        /* 去掉图片与文字之间的空隙 */
        vertical-align: middle;
        margin-bottom: 2px;
    }

</style>

item中用来插入图标和文字

item的图标设置

<div class="tab-bar-item" @click="itemClick"> 
    
    
	<!-- 判断是否active,注意这里用的是取反 -->
	<div v-if="!isActive" slot="item-icon">
       <slot name="item-icon"></slot>
    </div>
    
    <div v-else slot="item-icon-active">
       <slot name="item-icon-active"></slot>
    </div>
    
    <!-- 这里还要放文字 -->
   
</div>

item的文字设置

<div slot="item-text" :style="activeStyle">
   <slot name="item-text"></slot>
</div>

给图片和文字使用具名slot是因为TabBarItem也是一个抽离出来的单独组件,最终TabBar.vue和TabBarItem.vue都会集中在MainTabBar中引用并呈现。

<template>
    <tab-bar>
        <!-- 这里是第一个item-首页 -->
        <!-- 给子页面传递path和activeColor参数 -->
        <tab-bar-item path="/home" activeColor="blue">
            <!-- 图片的路径自己插 -->
            <img slot="item-icon" src="@assets/img/tabbar/home.svg" alt="">
            <img slot="item-icon-active" src="@assets/img/tabbar/home_active.svg" alt="">
            <!-- 这是某一项的文字 -->
            <div slot="item-text">首页</div>
        </tab-bar-item>
        <!-- 这里是第二个item-分类 -->
        <tab-bar-item path="/category" activeColor="green">
            <img slot="item-icon" src="@assets/img/tabbar/category.svg" alt="">
            <img slot="item-icon-active" src="@assets/img/tabbar/category_active.svg" alt="">
            <div slot="item-text">分类</div>
        </tab-bar-item>
    </tab-bar>
</template>

<script>
  // 引用两个组件
  import TabBar from "./tabbar/Tabbar";
  import TabBarItem from "./tabbar/TabbarItem";
  export default {
     
     
    name: "MainTabBar",
    // 注册两个组件
    components:{
     
     
      TabBar,
      TabBarItem
    }
  }
</script>

<style scoped>

</style>

层级关系是:

img、text -> tab-bar-item ≈ tab-bar -> main-tab-bar -> App

在MainTabBar.vue中引用并使用了这两个组件,并给子层传递了参数,所以在子层中要用props来接收一下:

这是TabBarItem的逻辑处理

<script>
    export default {
     
     
      name: "TabbarItem",
      props:{
     
     
        // 接收从MainTabBar传来的path和activeColor两个参数
        path: String,
        activeColor:{
     
     
          type:String,
          default : 'red'
        }
      },
      data(){
     
     
        return{
     
     
        }
      },
      computed : {
     
     
        // 判断一下是否被激活
        isActive() {
     
     
          return this.$route.path.indexOf(this.path) !== -1
        },
        // 被激活时的样式更改
        activeStyle() {
     
     
            return this.isActive ? {
     
     color : this.activeColor} : {
     
     }
        }
      },
      methods:{
     
     
        // 点击某个item,页面上显示对应页面
        itemClick(){
     
     
          this.$router.replace(this.path)
        }
      }
    }
</script>

该组件的最终实现需要:导航栏中对应的每一项图标和对应的展示页面

图标的路径在MainTabBar.vue中自行修改,对应的展示页面的话,需要安装vue-router并进行配置

这里结合懒加载简单配置“首页”和“分类”两个对应页面

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

// 引用两个要显示的页面的组件对应文件,使用的是懒加载
const Home = () => import('../views/home/Home');
const Category = () => import("../views/category/Category");

// 配置路由信息
const routes = [
  {
    
    
    path: '',
    redirect : '/home'
  },
  {
    
    
    path: '/home',
    component : Home
  },
  {
    
    
    path: '/category',
    component : Category
  }
]

// 使用的时'history'模式,
const router = new VueRouter({
    
    
  mode : 'history',
  routes
})

export default router

最终实现

在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/michaelxuzhi___/article/details/105892928