A Vue bottom navigation bar component

Write a separate component for the bottom navigation bar for easy use in future projects

Source address: https://github.com/michaelxuzhi/tabbar

Divided into two steps: creation and packaging

Requirements: independent, icon can be changed, name can be changed, flex layout

①Analysis: First is TabBar

Insert picture description here

TabBar is the main frame of the navigation bar, which wraps the entire navigation bar

Create 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>

Create the main navigation bar area MainTabBar.vue, here is actually the navigation bar area Tabbar.vue into an independent component, let it be displayed in the MainTabBar, because the subsequent implementation of the TabBarItem in the navigation bar area, so all have been extracted Separation and independence.

MainTabBar.vue

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

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

<style scoped>

</style>

Then reference the MainTabBar component in App.vue

<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>

Up to this point, a bottom navigation bar area has been created, but there is no content inside, so the height of Tabbar.vue is 0, and no effect can be seen. Just add a little height to the style of Tabbar.vue. I know what the navigation bar area looks like now. This hard-coded height will be removed later, and the navigation bar will be supported by the content of the TabBarItem

height: 50px;

Insert picture description here

② Then create TabBarItem, which is each option

These options are to be inserted into Tabbar.vue, so Tabbar.vue must set a slot so that each Item can be added

<slot></slot>

Insert picture description here

tabbar -> TabbarItem.vue

Insert picture description here

Display:flex has been set in the navigation bar; here the item is set to flex:1; the height of the item is given to 49px, which is the height of the usual navigation bar. If there is a height, the navigation bar can be opened, and the style of the icon in the item must be set.

<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>

Used to insert icons and text in item

item icon settings

<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 text settings

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

The reason for using named slots for pictures and text is that TabBarItem is also a separate component separated out. In the end, TabBar.vue and TabBarItem.vue will be referenced and presented in 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>

The hierarchical relationship is:

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

These two components are referenced and used in MainTabBar.vue, and parameters are passed to the sub-layer, so use props to receive it in the sub-layer:

This is the logical processing of 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>

The final realization of the component needs: each icon corresponding to the navigation bar and the corresponding display page

The path of the icon is modified by itself in MainTabBar.vue. If the corresponding display page is required, vue-router needs to be installed and configured

Here is a combination of lazy loading and simple configuration of two corresponding pages, "Home" and "Category"

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

Finally realized

Insert picture description here

Insert picture description here

Guess you like

Origin blog.csdn.net/michaelxuzhi___/article/details/105892928