【Vue学习笔记_16】案例-Tabbar导航栏

【Vue学习笔记_16】案例-Tabbar导航栏

这个案例结合了前面介绍的slot插槽、vue-router前端路由等知识点,最重要的是体现了组件封装的思想。

配套可执行代码示例 => GitHub

项目文件结构

  • assets/:静态资源,css、img等
  • components/:公共组件
  • pages/:页面组件,下面再为每个页面分设一个目录
  • router/:路由配置
  • App.vue:根组件
  • main.js:入口文件

这个案例要封装的就是components/下面的一个公共的Tabbar组件,因为可能在多个页面都需要导航栏。

【Vue学习笔记_16】案例-Tabbar导航栏

效果展示&组件结构

【Vue学习笔记_16】案例-Tabbar导航栏

封装TabBar

TabBarItem.vue

导航栏的最小元素。

选中时的图标、未选中时的图标、文字,都使用slot具名插槽的形式,提高复用性。

导航栏元素对应的路径path、选中时文字的样式activeColor,都使用props属性的形式,让使用者传入,也是为了提高复用性。

根据当前活跃的路由里是否包含props传进来的path,判断当前<router-view>展示的是否是该导航栏元素对应的页面组件,从而决定展示选中时的图标还是未选中时的图标,以及文字的样式。

绑定点击事件,用代码跳转路由到该导航栏元素对应的页面组件。

注:slot上不能直接绑定 class等属性,因为在使用时整个 slot会被替换掉,属性无法生效,也不建议绑定 v-if等指令。可以在 slot外面套一层 div,然后在 div上绑定属性或指令。

<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: {
      path: String,
      activeColor: {
        type: String,
        default: 'red'
      }
    },
    computed: {
      isActive() {
        //判断当前活跃的路由里是否包含props传进来的路径
        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: 1;
    text-align: center;
    height: 49px;
    font-size: 14px;
    margin-top: 3px;
  }
  .tab-bar-item img {
    width: 24px;
    height: 24px;
    vertical-align: middle;
    margin-bottom: 2px;
  }
</style>

TabBar.vue

为TabBarItem加一层包装,统一排版和样式。

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

<script>
  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,0.15);
  }
</style>

使用封装的TabBar

MainTabBar.vue

到了MainTabBar这一层,已经是业务相关的了。最外层使用<tab-bar>,给里面插入四个<tab-bar-item>,分别对应首页、分类、购物车、我的四个导航栏元素,给每个<tab-bar-item>里面再插入对应的选中时的图标、未选中时的图标、文字,以及传入path和activeColor属性。

<template>
  <div id="main-tab-bar">
    <tab-bar>
      <tab-bar-item path="/home" activeColor="red">
        <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>
      <tab-bar-item path="/category" activeColor="red">
        <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-item path="/shopcart" activeColor="red">
        <img slot="item-icon" src="../../assets/img/tabbar/shopcart.svg" alt="">
        <img slot="item-icon-active" src="../../assets/img/tabbar/shopcart_active.svg" alt="">
        <div slot="item-text">购物车</div>
      </tab-bar-item>
      <tab-bar-item path="/profile" activeColor="red">
        <img slot="item-icon" src="../../assets/img/tabbar/profile.svg" alt="">
        <img slot="item-icon-active" src="../../assets/img/tabbar/profile_active.svg" alt="">
        <div slot="item-text">我的</div>
      </tab-bar-item>
    </tab-bar>
  </div>
</template>

<script>
  import TabBar from '../tabbar/TabBar'
  import TabBarItem from '../tabbar/TabBarItem'

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

<style scoped>
</style>

App.vue

在根组件中使用<main-tab-bar>,和<router-view>展示的页面组件并列。

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

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

配置路由

router/index.js

配置页面组件和路径的对应关系。

import Vue from 'vue'
import Router from 'vue-router'

const Home = () => import('../pages/home/Home')
const Category = () => import('../pages/category/Category')
const ShopCart = () => import('../pages/shopcart/ShopCart')
const Profile = () => import('../pages/profile/Profile')

//1.安装路由插件
Vue.use(Router)

//2.创建路由对象
const routes = [
  {
    path: '',
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/category',
    component: Category
  },
  {
    path: '/cart',
    component: ShopCart
  },
  {
    path: '/profile',
    component: Profile
  }
]
const router = new Router({
  routes,
  mode: 'history'
})

//3.导出路由
export default router

文件目录配置别名

可以在build/webpack.base.conf.js中给文件目录配置别名,方便在项目中引用文件:

module.exports = {
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': resolve('src'),
      'assets': resolve('src/assets'),
      'components': resolve('src/components'),
      'pages': resolve('src/pages'),
    }
  }
}
上一篇:Vue3学习与实战 · 配置使用vue-router路由


下一篇:vue的router