Vue(小码哥王洪元)笔记07路由案例tabbar

1、tabbar案例01

进行了简单的布局

  • 1、创建一个样式文件base.css
body{margin:0; padding: 0;}
  • 2、修改App.vue
<template>
  <div id="app">
    <div id="tab-bar">
      <div class="tab-bar-item">首页</div>
      <div class="tab-bar-item">分类</div>
      <div class="tab-bar-item">购物车</div>
      <div class="tab-bar-item">我的</div>
    </div>
    
  </div>
</template>

<script>
export default {
  name: 'App',
 
}
</script>

<style>
/*引入样式文件*/
@import url('./assets/style/base');
#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.3);}
.tab-bar-item{flex: 1; text-align: center; height: 49px;}
</style>
2、tabbar案例02

这一讲主要是对组件各种封装和嵌套,其中嵌套使用插槽的方式进行多重嵌套

模板层面(从大到小层面)
App.vue—>TabBar.vue—>TabBarItem

代码层面层面(从小到大层面)
TabBarItem.vue—>TabBar.vue—>App.vue

1、TabBarItem.vue

在tab-bar-item放入两个具名插槽(包含name属性)<slot name=“item-icon”>和<slot name=“item-text”>

<template>
  <div class="tab-bar-item">
    <slot name="item-icon"></slot>
    <slot name="item-text"></slot>
  </div>
</template>

2、TabBar.vue

在TabBar中放入一个不具名插槽

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

2、App.vue

在最上层的模板中匹配所有插槽,注意,嵌套的div标签名是跟随id和class来的

<template>
  <div id="app">
    <tab-bar>
      <tab-bar-item>
        <img slot="item-icon" src="./assets/images/tabbar/home.svg"/>
        <div slot="item-text" >首页</div>
      </tab-bar-item>
      <tab-bar-item>
        <img slot="item-icon" src="./assets/images/tabbar/category.svg"/>
        <div slot="item-text">分类</div>
      </tab-bar-item>
      <tab-bar-item>
        <img slot="item-icon"  src="./assets/images/tabbar/shopcart.svg"/>
        <div slot="item-text">购物车</div>
      </tab-bar-item>
      <tab-bar-item>
        <img slot="item-icon"  src="./assets/images/tabbar/profile.svg"/>
        <div slot="item-text">我的</div>
      </tab-bar-item>
    </tab-bar>
  </div>
</template>
  • 1、拷贝各种资源图标
    将资源图标放在assets文件夹下的相关目录中

  • 2、创建最底层的组件页面TabBarItem.vue
    在页面中同时设计插槽内插入标签的样式(实际插槽内的具体div标签都是写好的,等最顶层App.vue文件创建具体标签的时候,再拷贝过去就行)

TabBarItem.vue

<template>
  <div class="tab-bar-item">
    <slot name="item-icon"></slot>
    <slot name="item-text"></slot>
    
  </div>
</template>

<script>
export default {
  name:'TabBarItem'
}
</script>
<style>
.tab-bar-item{flex: 1; text-align: center; height: 49px; font-size: 14px;}
.tab-bar-item img{ width: 24px; height: 24px; vertical-align: middle; margin-top: 2px;}
</style>
  • 2、创建中间层的组件页面TabBar.vue
    在这里放入一个插槽,用来防止整个导航条的插槽

TabBar.vue

<template>
  <div id="tab-bar">
      <slot></slot>
  </div>
</template>
<script>
export default {
  name:'TabBar'
}
</script>
<style>

#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.3);}

</style>
  • 3、修改最顶层组件页面App.vue
    在这一层,要匹配所有插槽

App.vue

<template>
  <div id="app">
    <tab-bar>
      <tab-bar-item>
        <img slot="item-icon" src="./assets/images/tabbar/home.svg"/>
        <div slot="item-text" >首页</div>
      </tab-bar-item>
      <tab-bar-item>
        <img slot="item-icon" src="./assets/images/tabbar/category.svg"/>
        <div slot="item-text">分类</div>
      </tab-bar-item>
      <tab-bar-item>
        <img slot="item-icon"  src="./assets/images/tabbar/shopcart.svg"/>
        <div slot="item-text">购物车</div>
      </tab-bar-item>
      <tab-bar-item>
        <img slot="item-icon"  src="./assets/images/tabbar/profile.svg"/>
        <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 url('./assets/style/base');
</style>
  • 4、文件目录
    在这里插入图片描述
  • 4、本集效果
    在这里插入图片描述
3、tabbar案例03插槽内容动态显示
  • 1、在app.vue中给图片多添加一个备选标签img。用于动态(根据标签值匹配)匹配插槽
<template>
  <div id="app">
    <tab-bar>
      <tab-bar-item>
        <img slot="item-icon" src="./assets/images/tabbar/home.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/homeactive.svg"/>
        <div slot="item-text" >首页</div>
      </tab-bar-item>
      <tab-bar-item>
        <img slot="item-icon" src="./assets/images/tabbar/category.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/categoryactive.svg"/>
        <div slot="item-text">分类</div>
      </tab-bar-item>
      <tab-bar-item>
        <img slot="item-icon"  src="./assets/images/tabbar/shopcart.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/shopcartactive.svg"/>
        <div slot="item-text">购物车</div>
      </tab-bar-item>
      <tab-bar-item>
        <img slot="item-icon"  src="./assets/images/tabbar/profile.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/profileactive.svg"/>
        <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 url('./assets/style/base');
</style>
  • 2、在TableBarItem.vue修改data和模板代码
    数据部分
 <script>
export default {
  name:'TabBarItem',
  data(){
    return{
      isActive:true
    }
  }
}
</script>

模板部分
添加一个插槽,同时由于动态显示的需要,一定要把插槽上层嵌套一个div
此外绑定class属性使用对象形式 :class="{ative:isActive}",这里类名为ative:isActive

<template>
  <div class="tab-bar-item">
    <div v-if="!isActive" >
      <slot name="item-icon"></slot>
    </div>
    <div v-else>
      <slot name="item-icon-active"></slot>
    </div>   
    <div :class="{ative:isActive}">
      <slot slot  name="item-text"></slot>
    </div>
  </div>
</template>

完整代码

<template>
  <div class="tab-bar-item">
    <div v-if="!isActive" >
      <slot name="item-icon"></slot>
    </div>
    <div v-else>
      <slot name="item-icon-active"></slot>
    </div>   
    <div :class="{ative:isActive}">
      <slot slot  name="item-text"></slot>
    </div>
    
    
  </div>
</template>

<script>
export default {
  name:'TabBarItem',
  data(){
    return{
      isActive:true
    }
  }
}
</script>
<style>
.tab-bar-item{flex: 1; text-align: center; height: 49px; font-size: 14px;}
.tab-bar-item img{ width: 24px; height: 24px; vertical-align: middle; margin-top: 2px;}
.ative{
  color:blue
}
</style>

效果:
在这里插入图片描述

4、tabbar案例04

在各个导航上添加导航跳转动作,通过路由跳转到不同的页面

  • 1、创建不同的页面

在src目录中单独创建views目录,并在views目录下创建不同的页面(.vue)

<template>
  <h2>分类</h2>
</template>
<script>
export default {
  name:'Category'
}
</script>
<style scoped>

</style>

在这里插入图片描述

  • 2、在路由index.js中导入路由信息
    首先要导入路由
    其次在设置路由映射
    router/index.js
import Vue from 'vue'
import Router from 'vue-router'
//导入组件,使用懒加载的方式
const Home = () => import('../views/home/Home.vue');
const Category = () => import('../views/category/Category.vue');
const Profile = () => import('../views/profile/Profile');
const Shopcart = () => import('../views/shopcart/Shopcart');

//使用路由对象
Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      redirect:'/Home'
    },
    {
      path: '/home',
      component:Home
    },
    {
      path: '/category',
      component:Category
    },
    {
      path: '/profile',
      component:Profile
    },
    {
      path: '/shopcart',
      component:Shopcart
    }
  ],
  //设置路由显示模式,history直接显示路径,哈希模式会显示一个#
  mode:'history'
})
  • 3、在TabeBarItem.vue中设置props用来向子组件传值
 //创建一个父组件向子组件传值的props
  props:{
    //指定一个字符串型的link属性
    path:String
  },

在模板代码中添加跳转方法

  <div class="tab-bar-item" @click="itemClick">
<script>
export default {
  name:'TabBarItem',
  //创建一个父组件向子组件传值的props
  props:{
    //指定一个字符串型的link属性
    path:String
  },
  data(){
    return{
      isActive:true,
    }
  },
  methods:{
    itemClick(){
      //路由跳转
      this.$router.replace(this.path)
    }
  }
}
</script>

完整代码

<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 :class="{ative:isActive}">
      <slot slot  name="item-text"></slot>
    </div>
    
    
  </div>
</template>

<script>
export default {
  name:'TabBarItem',
  //创建一个父组件向子组件传值的props
  props:{
    //指定一个字符串型的link属性
    path:String
  },
  data(){
    return{
      isActive:true,
    }
  },
  methods:{
    itemClick(){
     //解决两次点击报错问题,连续点击导致当前路由和点击的路由相同,导致重复替换
      if(this.$route.path==this.path){
        return
      }
      //路由跳转
      this.$router.replace(this.path)
    }
  }
}
</script>
<style>
.tab-bar-item{flex: 1; text-align: center; height: 49px; font-size: 14px;}
.tab-bar-item img{ width: 24px; height: 24px; vertical-align: middle; margin-top: 2px;}
.ative{
  color:blue
}
</style>
  • 3、在App.vue总组件中为每个item设置path
    这里的path值是要向props传递的值
  <tab-bar-item path="/home">

完整代码

<template>
  <div id="app">
    <tab-bar>
      <tab-bar-item path="/home">
        <img slot="item-icon" src="./assets/images/tabbar/home.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/homeactive.svg"/>
        <div slot="item-text" >首页</div>
      </tab-bar-item>
      <tab-bar-item path="/category">
        <img slot="item-icon" src="./assets/images/tabbar/category.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/categoryactive.svg"/>
        <div slot="item-text">分类</div>
      </tab-bar-item>
      <tab-bar-item path="/shopcart">
        <img slot="item-icon"  src="./assets/images/tabbar/shopcart.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/shopcartactive.svg"/>
        <div slot="item-text">购物车</div>
      </tab-bar-item>
      <tab-bar-item path="/profile">
        <img slot="item-icon"  src="./assets/images/tabbar/profile.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/profileactive.svg"/>
        <div slot="item-text">我的</div>
      </tab-bar-item>
    </tab-bar>
    <router-view></router-view>
  </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 url('./assets/style/base');
</style>
5、tabbar案例05解决不同状态下不同导航图标样式的问题

需求1:不同样式不同文字颜色
需求2:动态封装样式

  • 1、在文字颜色上使用动态样式绑定
    使用计算属性(activeStyle)动态绑定文本的样式
<div :style="activeStyle">
      <slot slot  name="item-text"></slot>
    </div>
  • 2、添加新的props属性
props:{
    //指定一个字符串型的link属性
    path:String,
    activeColor:{
      type:String,
      default:'blue'
    }
  },
  • 3、添加新的计算属性activeStyle**
    activeColor是通过app.vue的props传过来的
 computed:{
    isActive(){
      //如果当前path在路由映射表内,则返回true(隐式返回),不在数组里,indexOf会返回-1,这是indexOf函数
      return this.$route.path.indexOf(this.path)!==-1
    },
    activeStyle(){
      //根据isActive计算属性的值,返回不同的color值
      return this.isActive?{color:this.activeColor}:{}
    }
  },
  • 4、原有.active的演示文件不用了
/*
//由于在TabeBarItem文件里直接设置activColor,则不需要这个样式了
.ative{
  color:blue
}
*/

完整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 slot  name="item-text"></slot>
    </div>
    
    
  </div>
</template>

<script>
export default {
  name:'TabBarItem',
  //创建一个父组件向子组件传值的props
  props:{
    //指定一个字符串型的link属性
    path:String,
    activeColor:{
      type:String,
      default:'blue'
    }
  },
  data(){
    return{
      //isActive:true,
    }
  },
  computed:{
    isActive(){
      //如果当前path在路由映射表内,则返回true(隐式返回),不在数组里,indexOf会返回-1,这是indexOf函数
      return this.$route.path.indexOf(this.path)!==-1
    },
    activeStyle(){
      //根据isActive计算属性的值,返回不同的color值
      return this.isActive?{color:this.activeColor}:{}
    }
  },

  methods:{
    itemClick(){
      //解决两次点击报错问题
      if(this.$route.path==this.path){
        return
      }
      //路由跳转
      this.$router.replace(this.path)
    }
  }
}
</script>
<style>
.tab-bar-item{flex: 1; text-align: center; height: 49px; font-size: 14px;}
.tab-bar-item img{ width: 24px; height: 24px; vertical-align: middle; margin-top: 2px;}
/*
//由于在TabeBarItem文件里直接设置activColor,则不需要这个样式了
.ative{
  color:blue
}
*/
</style>
  • 5、App.vue模板中,把props的值传过去
 <tab-bar-item path="/home" activeColor="blue">
        <img slot="item-icon" src="./assets/images/tabbar/home.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/homeactive.svg"/>
        <div slot="item-text" >首页</div>
      </tab-bar-item>

完整代码:

<template>
  <div id="app">
    <tab-bar>
      <tab-bar-item path="/home" activeColor="blue">
        <img slot="item-icon" src="./assets/images/tabbar/home.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/homeactive.svg"/>
        <div slot="item-text" >首页</div>
      </tab-bar-item>
      <tab-bar-item path="/category" activeColor="blue">
        <img slot="item-icon" src="./assets/images/tabbar/category.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/categoryactive.svg"/>
        <div slot="item-text">分类</div>
      </tab-bar-item>
      <tab-bar-item path="/shopcart" activeColor="blue">
        <img slot="item-icon"  src="./assets/images/tabbar/shopcart.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/shopcartactive.svg"/>
        <div slot="item-text">购物车</div>
      </tab-bar-item>
      <tab-bar-item path="/profile" activeColor="blue">
        <img slot="item-icon"  src="./assets/images/tabbar/profile.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/profileactive.svg"/>
        <div slot="item-text">我的</div>
      </tab-bar-item>
    </tab-bar>
    <router-view></router-view>
  </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 url('./assets/style/base');
</style>

效果
在这里插入图片描述

6、整个项目完整代码
  • 1、单个页面Category.vue

其他文件也一样,不再重复写

<template>
  <h2>分类</h2>
</template>
<script>
export default {
  name:'Category'
}
</script>
<style scoped>

</style>
  • 2、index.js(路由目录下)
import Vue from 'vue'
import Router from 'vue-router'
//导入组件,使用懒加载的方式
const Home = () => import('../views/home/Home.vue');
const Category = () => import('../views/category/Category.vue');
const Profile = () => import('../views/profile/Profile');
const Shopcart = () => import('../views/shopcart/Shopcart');

//使用路由对象
Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      redirect:'/Home'
    },
    {
      path: '/home',
      component:Home
    },
    {
      path: '/category',
      component:Category
    },
    {
      path: '/profile',
      component:Profile
    },
    {
      path: '/shopcart',
      component:Shopcart
    }
  ],
  //设置路由显示模式,history直接显示路径,哈希模式会显示一个#
  mode:'history'
})
  • 3、TabVBar.vue
<template>
  <div id="tab-bar">
      <slot></slot>
  </div>
</template>
<script>
export default {
  name:'TabBar'
}
</script>
<style>

#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.3);}

</style>
  • 4、TabVBarItem.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 slot  name="item-text"></slot>
    </div>
    
    
  </div>
</template>

<script>
export default {
  name:'TabBarItem',
  //创建一个父组件向子组件传值的props
  props:{
    //指定一个字符串型的link属性
    path:String,
    activeColor:{
      type:String,
      default:'blue'
    }
  },
  data(){
    return{
      //isActive:true,
    }
  },
  computed:{
    isActive(){
      //如果当前path在路由映射表内,则返回true(隐式返回),不在数组里,indexOf会返回-1,这是indexOf函数
      return this.$route.path.indexOf(this.path)!==-1
    },
    activeStyle(){
      //根据isActive计算属性的值,返回不同的color值
      return this.isActive?{color:this.activeColor}:{}
    }
  },

  methods:{
    itemClick(){
      //解决两次点击报错问题
      if(this.$route.path==this.path){
        return
      }
      //路由跳转
      this.$router.replace(this.path)
    }
  }
}
</script>
<style>
.tab-bar-item{flex: 1; text-align: center; height: 49px; font-size: 14px;}
.tab-bar-item img{ width: 24px; height: 24px; vertical-align: middle; margin-top: 2px;}
/*
//由于在TabeBarItem文件里直接设置activColor,则不需要这个样式了
.ative{
  color:blue
}
*/
</style>
  • 5、App.vue
<template>
  <div id="app">
    <tab-bar>
      <tab-bar-item path="/home" activeColor="blue">
        <img slot="item-icon" src="./assets/images/tabbar/home.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/homeactive.svg"/>
        <div slot="item-text" >首页</div>
      </tab-bar-item>
      <tab-bar-item path="/category" activeColor="blue">
        <img slot="item-icon" src="./assets/images/tabbar/category.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/categoryactive.svg"/>
        <div slot="item-text">分类</div>
      </tab-bar-item>
      <tab-bar-item path="/shopcart" activeColor="blue">
        <img slot="item-icon"  src="./assets/images/tabbar/shopcart.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/shopcartactive.svg"/>
        <div slot="item-text">购物车</div>
      </tab-bar-item>
      <tab-bar-item path="/profile" activeColor="blue">
        <img slot="item-icon"  src="./assets/images/tabbar/profile.svg"/>
        <img slot="item-icon-active" src="./assets/images/tabbar/profileactive.svg"/>
        <div slot="item-text">我的</div>
      </tab-bar-item>
    </tab-bar>
    <router-view></router-view>
  </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 url('./assets/style/base');
</style>
  • 6、目录结构
    在这里插入图片描述

最终效果
在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值