TabBar

自定义vue底部导航栏组件tabbar,效果如下所示:
请添加图片描述

1. 基本结构搭建

  • 根据组件封装的思想,我们事先创建几个文件夹,用来存放对应部分的代码。文件结果如下图所示:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N4uYEM9r-1630891968053)(C:\Users\xmydd\AppData\Roaming\Typora\typora-user-images\image-20210905172933000.png)]

  • 从效果图来看底部可以分为两个部分:最外层框架区和内层图标区
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hlezzVxI-1630891968059)(C:\Users\xmydd\AppData\Roaming\Typora\typora-user-images\image-20210905173258650.png)]

  • 组件就是需要的时候可以直接引用并且方便更改,那么我们就可以将tabbar导航栏组件划分为两个小的组件:TabBar.vueTabBarItem.vue

    • TabBar.vue设置导航栏组件的外观形状
    • TabBarItem.vue负责设置内置图标汉字的样式

2. slot插槽的使用

组件里的图标和汉字我们都是不能写死的,所以就要用到<slot>插槽对要填充内容的区域进行占位。

3. TabBar.vue

<template>
    <div id="tab-bar">
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: "TabBar",
}
</script>

<style scoped>
    #tab-bar{
        background: #f2f2f2;
        display: flex;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        box-shadow: 0px -1px 1px rgba(100, 100, 100, 0.2);
    }
</style>
  • App.vue中使用

    • TabBar组件的源码中,我们使用了slot插槽进行占位,那么当我们需要填充内容时,直接将内容写入<tab-bar></tab-bar>标签里即可。
    <template>
    	<div id="app">
            <tab-bar></tab-bar>
        </div>
    </template>
    <script>
    import TabBar from "./components/tabbar/TabBar"
    
    export default {
      name: 'App',
      components: {
        TabBar,
      },
    }
    </script>
    

4. TabBarItem.vue

<template>
    <div class="tab-bar-item" @click="itemClick">
        <!-- 具名插槽 -->
        <div><slot name="item-icon"></slot></div>
        <div><slot name="item-text"></slot></div>
    </div>
</template>

<script>
export default {
    name: "TabBarItem",
    data(){
        return{}
    },
}
</script>

<style scoped>
    .tab-bar-item{
        height: 49px;
        font-size: 14px;
        flex: 1;
        text-align: center;
    }
    .tab-bar-item img{
        margin-top: 3px;
        vertical-align: middle;
        margin-bottom: 2px;
    }
</style>
  • App.vue中使用

    • 这里我只引用了一次<tab-bar-item>,按照需求我们是需要重复引用四次的
    • 因为TabBarItem.vue中使用的是具名插槽,所以填入的内容也许加上对应插槽的名字,以便替换到正确的位置。
    <template>
      <div id="app">
        <tab-bar>
          <tab-bar-item>
            <img slot="item-icon" src="./assets/img/home_page.svg" alt="">
            <div slot="item-text">首页</div>
          </tab-bar-item>
        </tab-bar>
      </div>
    </template>
    
    <script>
    import TabBar from "./components/tabbar/TabBar"
    import TabBarItem from "./components/tabbar/TabBarItem"
    
    export default {
      name: 'App',
      components: {
        TabBar,
        TabBarItem
      },
    }
    </script>
    
    <style>
    @import "./assets/css/common.css";
    </style>
    
    

5. 路由的使用

完成上面四步就能的到一个基本的框架,最后把组件跟其他页面通过路由连接起来,就能实现页面的跳转

5.1 配置路由
  • router.js
// 1.配置路由相关信息
import Vue from 'vue'
import VueRouter from 'vue-router'

const Home = () => import("../views/home/Home")
const Shopping = () => import('../views/shopping/Shopping')
const Sort = () => import('../views/sort/Sort')
const My = () => import('../views/my/My')

// 2. 安装路由插件
// 所有插件在使用时都要用到,Vue.use()
Vue.use(VueRouter);

// 3. 创建VueRouter对象
const routes = [
    {
        path: '/',
        redirect: '/home'
    },
    {
        path: '/home',
        component: Home
    },
    {
        path: '/shopping',
        component: Shopping
    },
    {
        path: '/sort',
        component: Sort
    },
    {
        path: '/my',
        component: My
    }
];

const router = new VueRouter({
    // 配置路由和组件之间的应用关系
    routes,
    mode: 'history'
});

// 4. 将路由实例对象倒出
export default router

  • main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router/router'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  router,
}).$mount('#app')
5.2 使用路由
  • 因为我们是通过点击TabBarItem.vuetab-bar-item区域,从而实现页面跳转的。那么我们可以对他进行监听

    • 给他绑定一个点击事件,当点击这个区域以后实现页面的跳转
    <template>
        <div class="tab-bar-item" @click="itemClick"></div>
    </template>
    
    methods:{
        itemClick(){
            // 当前活跃路由路径跟传过来的不一致时才进行路由跳转
            if(this.$route.path != this.link){
                this.$router.push(this.link);
            }
        }
    }
    
  • 实现父子组件通信

    • 设置props,在父组件App.vue中给每一个``tab-bar-item`标签绑定路由地址,再将地址传给子组件

      • App.vue
      <template>
        <div id="app">
          <tab-bar>
            <tab-bar-item link="/home" activeColor="#ff6979">
              <img slot="item-icon" src="./assets/img/home_page.svg" alt="">
              <img slot="item-icon-active" src="./assets/img/home-page-active.svg" alt="">
              <div slot="item-text">首页</div>
            </tab-bar-item>
          </tab-bar>
      
          <!-- 视图区 -->
          <router-view></router-view>
        </div>
      </template>
      
      • TabBarItem.vue
      export default{
          name: "TabBarItem",
          props:{
              link: String,
          },
      }
      

6. 动态改变样式

最后我们来实现点击前后改变图片和文字的样式,点击前为黑色,点击后变为红色

  • 图标点击后变红色

    • TabBarItem.vue中再添加一个插槽,点击后替换掉原来那个
    • 设置计算属性isActive,给两个图标插槽绑定v-ifelse
    • 当前活跃路由路径跟传过来的一致时isActive为true,显示为红色
  • 字体点击后变红

    • 实现父子组件通信,通过propsactiveColor(设置字体颜色)传给父组件,在接收父组件传回的值
    • 给插槽动态绑定style,设置一个计算属性activeStyle,如果isActive为true则显示this.activeColor
  • 完整代码

    • App.vue
    <template>
      <div id="app">
        <tab-bar>
          <tab-bar-item link="/home" activeColor="#ff6979">
            <img slot="item-icon" src="./assets/img/home_page.svg" alt="">
            <img slot="item-icon-active" src="./assets/img/home-page-active.svg" alt="">
            <div slot="item-text">首页</div>
          </tab-bar-item>
        </tab-bar>
    
        <!-- 视图区 -->
        <router-view></router-view>
      </div>
    </template>
    
    • TabBarItem.vue
    <template>
        <div class="tab-bar-item" @click="itemClick">
            <!-- 具名插槽 -->
            <div v-if="!isActive"><slot name="item-icon"></slot></div>
            <div v-else><slot name="item-icon-active"></slot></div>
            <div :style="activeStyle"><slot name="item-text"></slot></div>
        </div>
    </template>
    
    <script>
    export default {
        name: "TabBarItem",
        props:{
            link: String,
            activeColor:{
                type: String,
            }
        },
        data(){
            return{
                
            }
        },
        computed:{
            isActive(){
                // 当前活跃路由路径跟传过来的是否一致
                // 一致返回true
                return this.$route.path.includes(this.link);
            },
            activeStyle(){
                return this.isActive ? {color: this.activeColor} : {};
            }
        },
        methods:{
            itemClick(){
                // 当前活跃路由路径跟传过来的不一致时才进行路由跳转
                if(this.$route.path != this.link){
                    this.$router.push(this.link);
                }
            }
        }
    }
    </script>
    
    <style scoped>
        .tab-bar-item{
            height: 49px;
            font-size: 14px;
            flex: 1;
            text-align: center;
        }
        .tab-bar-item img{
            margin-top: 3px;
            vertical-align: middle;
            margin-bottom: 2px;
        }
    </style>
    
Logo

前往低代码交流专区

更多推荐