封装TabBar案例

封装TabBar案例

TabBar.png

通过学习上面这个TabBar的封装,感受一下组件化开发和封装的思想。

一、第一步

先写死,就写4个条目,分别是首页,分类,购物车,我的,先写在Vue.app中,把tab-bar的css样式和tab-bar-item的样式调整好

<template>
  <div id="app">
    <div id="tab-bar">
      <div class="tab-bar-item">
        <img src="./assets/img/tabbar/home.svg" alt="...">
        <div class="item-text">首页</div>
      </div>
      <div class="tab-bar-item">
        <img src="./assets/img/tabbar/category.svg" alt="...">
        <div class="item-text">分类</div>
      </div>
      <div class="tab-bar-item">
        <img src="./assets/img/tabbar/shopcart.svg" alt="...">
        <div class="item-text">购物车</div>
      </div>
      <div class="tab-bar-item">
        <img src="./assets/img/tabbar/profile.svg" alt="...">
        <div class="item-text">我的</div>
      </div>
    </div>
  </div>
</template>

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

<style>
/* 在style中引用文件这样子使用@import + url */
  @import './assets/css/base.css';
  #tab-bar{
    /* 流体布局,使得这个元素占据一行 */
    display: flex;
    height: 49px;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #f6f6f6;
    font-size: 14px;
    /*
    <length>①: 第 1 个长度值定义元素的阴影水平偏移值。正值,阴影出现在元素右侧;负值,则阴影出现在元素左侧 

    <length>②: 第 2 个长度值定义元素的阴影垂直偏移值。正值,阴影出现在元素底部;负值,则阴影出现在元素顶部 

    <length>③: 第 3 个长度值定义元素的阴影模糊值半径(如果提供了)。该值越大阴影边缘越模糊,若该值为0,阴影边缘不出现模糊。不允许负值 <length>④: 第 4 个长度值定义元素的阴影外延值(如果提供了)。正值,阴影将向四面扩展;负值,则阴影向里收缩 

    <color>: 定义元素阴影的颜色。如果该值未定义,阴影颜色将默认取当前最近的文本颜色 inset: 定义元素的阴影类型为内阴影。该值为空时,则元素的阴影类型为外阴影 
    */
    box-shadow: 0 -3px 1px rgba(100,100,100,.08);
  }
  .tab-bar-item{
    /* 使得这些元素均等分布在流体元素中 */
    flex: 1;
    text-align: center;
  }
  .tab-bar-item img{
    width: 24px;
    height: 24px;
    /* 使得图片下面不要离文字太远 */
    /* middle: 把当前盒的垂直中心和父级盒的基线加上父级的半x-height对齐 */
    vertical-align: middle;
  }
</style>

第二步、把tab-bar抽成一个独立的组件

TabBar组件

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

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

<style scoped>
#tab-bar{
    display: flex;
    height: 49px;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #f6f6f6;
    font-size: 14px;
    box-shadow: 0 -3px 1px rgba(100,100,100,.08);
  }
</style>

第三步、将TabBar中的每个条目抽成一个独立的组件tab-bar-item

tab-bar-item组件

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

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

<style scoped>
  .tab-bar-item{
    flex: 1;
    text-align: center;
  }
  .tab-bar-item img{
    width: 24px;
    height: 24px;
    vertical-align: middle;
  }
</style>

第四步、在进行完上面两步后,现在的App.vue长这样

<template>
  <div id="app">
    <tab-bar>
      <tab-bar-item>
        <img src="./assets/img/tabbar/home.svg" alt="..." slot="item-icon">
        <img src="./assets/img/tabbar/home_active.svg" alt="..." slot="item-icon-active">
        <div slot="item-text">首页</div>
      </tab-bar-item>
      <tab-bar-item>
        <img src="./assets/img/tabbar/category.svg" alt="..." slot="item-icon">
        <img src="./assets/img/tabbar/category_active.svg" alt="..." slot="item-icon-active">
        <div slot="item-text">分类</div>
      </tab-bar-item>
      <tab-bar-item>
        <img src="./assets/img/tabbar/shopcart.svg" alt="..." slot="item-icon">
        <img src="./assets/img/tabbar/shopcart_active.svg" alt="..." slot="item-icon-active">
        <div slot="item-text">购物车</div>
      </tab-bar-item>
      <tab-bar-item>
        <img src="./assets/img/tabbar/profile.svg" alt="..." slot="item-icon">
        <img src="./assets/img/tabbar/profile_active.svg" alt="..." slot="item-icon-active">
        <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>
/* 在style中引用文件这样子使用@import + url */
  @import './assets/css/base.css';
</style>

第五步、配置路由

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

//使用懒加载来导入组件
const Home = () => import('../components/Home')  
const Category = () => import('../components/Category')  
const Profile = () => import('../components/Profile')  
const ShopCart = () => import('../components/ShopCart')  

//1.注入插件
Vue.use(Router)
//2.创建路由配置对象
const routes = [
  {
    path:'',
      //默认显示首页,重定向
    redirect:'/home'
  },
  {
    path:'/home',
    component:Home
  },
  {
    path:'/category',
    component:Category
  },
  {
    path:'/shopCart',
    component:ShopCart
  },
  {
    path:'/profile',
    component:Profile
  }
]

export default new Router({
  routes,
    //使用H5的history模式改变url
  mode:'history'
})

第六步、通过代码跳转路由,设置活跃时候的字体颜色可由外界传入

<template>
  <div class="tab-bar-item" @click="itemClick">
    <div v-if="isActive"><slot name="item-icon-active"></slot></div>
    <div v-else><slot name="item-icon"></slot></div>
    <div class="item-text" :style="finalColor">
      <slot name="item-text"></slot>
    </div>
  </div>
</template>

<script>
export default {
  name:"TabBarItem",
  data(){
    return {
      
    }
  },
  props:{
    path:{
      type:String,
      require:true
    },
    color:{
      type:String,
      default:"red"
    }
  }
  ,
  computed:{
    isActive(){
      return this.$route.path.indexOf(this.path) !== -1;
    },
    finalColor(){
      return this.isActive?{color:this.color}:{};
    }
  },
  methods:{
    itemClick(){
      // 判断是否正处于活跃状态,若是的话不进行跳转
      this.$route.path.indexOf(this.path) !== -1?void(0):this.$router.replace(this.path);
    }
  }
}
</script>

<style scoped>
  .tab-bar-item{
    flex: 1;
    text-align: center;
  }
  .tab-bar-item img{
    width: 24px;
    height: 24px;
    vertical-align: middle;
  }
</style>

第七步、将最终的TabBar抽成组件,使得App中的代码减少

<template>
  <div>
    <tab-bar>
      <tab-bar-item path='/home' color="deepPink">
        <img src="@/assets/img/tabbar/home.svg" alt="..." slot="item-icon">
        <img src="@/assets/img/tabbar/home_active.svg" alt="..." slot="item-icon-active">
        <div slot="item-text">首页</div>
      </tab-bar-item>
      <tab-bar-item path='/category' color="deepPink">
        <img src="@/assets/img/tabbar/category.svg" alt="..." slot="item-icon">
        <img src="@/assets/img/tabbar/category_active.svg" alt="..." slot="item-icon-active">
        <div slot="item-text">分类</div>
      </tab-bar-item>
      <tab-bar-item path='/shopCart' color="deepPink">
        <img src="@/assets/img/tabbar/shopcart.svg" alt="..." slot="item-icon">
        <img src="@/assets/img/tabbar/shopcart_active.svg" alt="..." slot="item-icon-active">
        <div slot="item-text">购物车</div>
      </tab-bar-item >
      <tab-bar-item path='/profile' color="deepPink">
        <img src="@/assets/img/tabbar/profile.svg" alt="..." slot="item-icon">
        <img src="@/assets/img/tabbar/profile_active.svg" alt="..." slot="item-icon-active">
        <div slot="item-text">我的</div>
      </tab-bar-item>
    </tab-bar>
  </div>
</template>

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

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

<style scoped>

</style>

到了这里,基本的功能差不多实现了,不过,我注意到一点,就是频繁的复制粘贴代码片段的时候,其中的一些涉及到路径的东西很容易出问题,因为写的是相对路径,所以为了使代码更加强壮,可移植性更强,我们应该把路径从src开始写起,但是一个个都这样的话代码量太大了,而且没什么意义,所以我们可以给路径改名

第八步、路径改别名

在使用Vue/CLI2.xc创建的项目中,可以打开build/webpack.base.conf.js文件,找到这里进行路径改别名

resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': resolve('src'),
      
    }
  },

现在@已经映射了src了,我们可以继续添加一些别名

resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': resolve('src'),
      'assets':resolve('src/assets'),
      'components':resolve('src/components'),
      'views':resolve('src/components/views')
    }
  },

注意:使用别名的时候有两种情况

  • 在import或者require 中使用别名

    这时候直接写别名就可以了

  • 在其它情况下使用别名

    这时候在别名前面要加一个"~"

    使用别名后

    <template>
      <div>
        <tab-bar>
          <tab-bar-item path='/home' color="deepPink">
            <img src="~assets/img/tabbar/home.svg" alt="..." slot="item-icon">
            <img src="~assets/img/tabbar/home_active.svg" alt="..." slot="item-icon-active">
            <div slot="item-text">首页</div>
          </tab-bar-item>
          <tab-bar-item path='/category' color="deepPink">
            <img src="~assets/img/tabbar/category.svg" alt="..." slot="item-icon">
            <img src="~assets/img/tabbar/category_active.svg" alt="..." slot="item-icon-active">
            <div slot="item-text">分类</div>
          </tab-bar-item>
          <tab-bar-item path='/shopCart' color="deepPink">
            <img src="~assets/img/tabbar/shopcart.svg" alt="..." slot="item-icon">
            <img src="~assets/img/tabbar/shopcart_active.svg" alt="..." slot="item-icon-active">
            <div slot="item-text">购物车</div>
          </tab-bar-item >
          <tab-bar-item path='/profile' color="deepPink">
            <img src="~assets/img/tabbar/profile.svg" alt="..." slot="item-icon">
            <img src="~assets/img/tabbar/profile_active.svg" alt="..." slot="item-icon-active">
            <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:"FinalTabBar",
      components:{
        TabBar,
        TabBarItem
      }
    }
    </script>
    
    <style scoped>
    
    </style>
    

    对配置文件进行修改之后一定要重新跑一下项目才会生效

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容