1. 实现Tab-Bar思路

  1. 下方单独的Tab-Bar组件如何封装?
    • 自定义Tab-Bar组件,在APP中使用
    • Tab-Bar位置在底部,并设置你需要的样式
  2. Tab-Bar中显示的内容由外部决定
    • 定义插槽
    • flex布局平分Tab-Bar
  3. 自定义Tab-Bar-Item,可以传入图片和文字
    • 定义Tab-Bar-Item,并定义两个插槽:图片和文字
    • 给插槽外层包装div,设置样式
    • 填充插槽,实现底部Tab-Bar的效果
  4. 传入高亮图片
    • 定义另一个插槽,插入active-icon的数据
    • 定义一个变量isActicve,通过v-show来决定是否显示对应的icon
  5. Tab-Bar-Item绑定路由数据
    • 安装路由:npm install vue-router --save
    • router/index.js配置路由信息,并创建对应的组件
    • main.js中注册router
    • App.vue中使用router-linkrouter-view
  6. 点击item跳转到对应的路由,并且动态决定isActive
    • 监听item的点击,通过this.$router.replace()替换路由路径
    • 通过this.$route.path.indexOf(this.link)!==-1来判断是否使active
  7. 动态计算active样式
    • 封装新的计算属性:this.isActive?{'color': 'red'}:{}

2. 代码实现

使用vue init webpack 02-vue-router-tabbar-v1新建一个项目工程(使用vuecli2)。

  1. 在文件夹assest下新建css/base.css,用于初始化css

    base.css

    1
    2
    3
    4
    body {
    padding: 0;
    margin: 0;
    }

    修改App.vue,添加初步样式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    <template>
    <div id="app">
    <div id="tar-bar">
    <div class="tar-bar-item">首页</div>
    <div class="tar-bar-item">分类</div>
    <div class="tar-bar-item">购物车</div>
    <div class="tar-bar-item">我的</div>
    </div>
    </div>
    </template>

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

    <style>
    /* style中引用使用@import */
    @import url('./assets/css/base.css');

    #tar-bar {
    display: flex;
    background-color: #f6f6f6;

    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;

    box-shadow: 0 -1px 1px rgba(100, 100, 100, .2);
    }

    .tar-bar-item {
    flex: auto;
    text-align: center;
    height: 49px;
    font-size: 20px;
    }
    </style>

    使用npm run dev,查看网页效果

                                                ![](https://gitee.com/krislin_zhao/IMGcloud/raw/master/img/20200518092055.png)
    

    思考:如果每次都要复用tabbar,那每次都需要复制粘贴,应该要把tabbar抽离出来,vue就是组件化思想。

  2. 将tabbar抽离成组件

    在components下新建tabbar文件夹,新建TarBar.vueTabBarItem.vue,TabBarItem组件是在组件TarBar中抽取出来的,可以传入图片和文字(比如首页),所有需要使用插槽<slot>代替。

    TarBar.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <template>
    <div id="tab-bar">
    <!-- 插槽代替tabbaritem -->
    <slot></slot>
    </div>
    </template>
    <script type="text/ecmascript-6">
    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, .2);
    }
    </style>

    TabBar弄一个slot插槽用于插入TabBarItem组件(可能插入多个).

    TabBarItem.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
<div class="tab-bar-item">
<!-- item-icon表示图片插槽 item-text表示文字插槽,例如首页 -->
<slot name="item-icon"></slot>
<slot name="item-text"></slot>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: 'TabBarItem'
}
</script>
<style scoped>
.tab-bar-item {
flex: auto;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
height: 24px;
width: 24px;
margin-top: 3px;
vertical-align: middle;
margin-bottom: 2px;
}
</style>

TabBarItem组件中插入2个插槽一个用于插入图片一个用于插入文字。

MainTabBar.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<template>
<div class="main-tab-bar">
<TabBar>
<TabBarItem path="/home" activeColor="blue">
<img slot="item-icon" src="/note_image/Vue/vue2/~assets/img/tabbar/home.png" alt="" srcset="">
<template v-slot:item-text>
<div>首页</div>
</template>
</TabBarItem>
<TabBarItem path="/categories">
<template #item-icon>
<img src="/note_image/Vue/vue2/~assets/img/tabbar/categories.png" alt="" srcset="">
</template>
<template #item-text>
<div>分类</div>
</template>
</TabBarItem>
<TabBarItem path="/shop">
<template #item-icon>
<img src="/note_image/Vue/vue2/~assets/img/tabbar/shopcart.png" alt="" srcset="">
</template>
<template #item-text>
<div>购物车</div>
</template>
</TabBarItem>
<TabBarItem path="/me">
<template #item-icon>
<img src="/note_image/Vue/vue2/~assets/img/tabbar/profile.png" alt="" srcset="">
</template>
<template #item-text>
<div>我的</div>
</template>
</TabBarItem>
</TabBar>
</div>
</template>
<script type="text/ecmascript-6">
import TabBar from "@/components/tabbar/TabBar"
import TabBarItem from "@/components/tabbar/TabBarItem"
export default {
name: "MainTabBar",
components: {
TabBar,
TabBarItem
}
}
</script>
<style scoped>
</style>

在MainTabBar组件中加入另外2个组件。

注意此处使用~assets@/components是使用了别名配置,详情请看3.别名配置

最后在app.vue中导入MainTabBar组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div id="app">
<MainTabBar></MainTabBar>
</div>
</template>
<script>
import MainTabBar from '@/components/MainTabBar'
export default {
name: 'App',
components: {
MainTabBar
}
}
</script>
<style>
/* style中引用使用@import */
@import url('./assets/css/base.css');
</style>

效果如图所示,将组件进行了分离重组,只要修改MainTabBar组件就可以修改图片和文字描述,可以复用。

  1. 如何实现点击首页首页字体变红图片变红色

    这里需要用到路由的active-class

    思路:引用2张图片,一张是正常颜色一张是红色,使用v-ifv-else来处理是否变色,在路由处于活跃状态的时候,变红色。

    引入路由使用路由就不细说了,这里仅贴上tabbar的修改代码。

    TabBarItem.vue组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <template>
    <div class="tab-bar-item" :style="activeStyle" @click="itemClick">
    <!-- item-icon表示图片插槽 item-text表示文字插槽,例如首页 -->
    <div v-if="!isActive">
    <slot name="item-icon"></slot>
    </div>
    <div v-else>
    <slot name="item-icon-active"></slot>
    </div>
    <div :class="{active:isActive}">
    <slot name="item-text"></slot>
    </div>
    </div>
    </template>

    <script type="text/ecmascript-6">
    export default {
    name: 'TabBarItem',
    props:{
    path:String,
    activeColor:{
    type:String,
    default:'red'
    }
    },
    computed: {
    isActive(){
    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: auto;
    text-align: center;
    height: 49px;
    font-size: 14px;
    }

    .tab-bar-item img {
    height: 24px;
    width: 24px;
    margin-top: 3px;
    vertical-align: middle;
    margin-bottom: 2px;
    }
    </style>
    1. 使用props获取传递的值,这里传递是激活颜色,默认是红色
    2. 设置计算属性isActiveactiveStyle,分别表示激活状态和激活的样式
    3. 定义itemClick()方法用于获取点击事件,点击后使用代码实现路由跳转,这里使用默认的hash模式
    4. 使用v-ifv-else来进行条件判断

    MainTabBar.vue组件

    1
    2
    3
    4
    5
    6
    7
    <TabBarItem path="/home">
    <img slot="item-icon" src="/note_image/Vue/vue2/~assets/img/tabbar/home.png" alt="" srcset="">
    <img slot="item-icon-active" src="/note_image/Vue/vue2/~assets/img/tabbar/home_active.png" alt="" srcset="">
    <template v-slot:item-text>
    <div>首页</div>
    </template>
    </TabBarItem>

    添加激活状态的图片与未激活的图片并列。

  2. 配置路由信息,参考之前的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import Vue from 'vue'
    import Router from 'vue-router'


    Vue.use(Router)

    const routes = [
    {
    path: '/',
    redirect: '/home'//缺省时候重定向到/home
    },
    {
    path: '/home',
    component: () => import ('../views/home/Home.vue')
    },
    {
    path: '/categories',
    component: () => import ('../views/categories/Categories.vue')
    },
    {
    path: '/shop',
    component: () => import ('../views/shop/Shop.vue')
    },
    {
    path: '/profile',
    component: () => import ('../views/profile/Profile.vue')
    },
    ]

    export default new Router({
    routes,
    // linkActiveClass:"active"
    })

  3. 修改main.js和App.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import Vue from 'vue'
    import App from './App'
    import router from './router'

    Vue.config.productionTip = false

    /* eslint-disable no-new */
    new Vue({
    el: '#app',
    router,
    render: h => h(App)
    })
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <template>
    <div id="app">
    <router-view></router-view>
    <MainTabBar></MainTabBar>
    </div>
    </template>

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

    <style>
    /* style中引用使用@import */
    @import url('./assets/css/base.css');

    </style>

3. 别名配置

经常的我们向引入图片文件等资源的时候使用相对路径,诸如../assets/xxx这样的使用../获取上一层,如果有多个上层就需要../../xxx等等这样不利于维护代码。此时就需要一个能获取到指定目录的资源的就好了。

配置

webpack.base.config中配置使用别名,找到resolve:{}模块,增加配置信息

1
2
3
4
5
6
7
8
9
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'assets': resolve('src/assets'),
'components': resolve('src/components'),
'views': resolve('scr/views')
}
},

这里@指定目录是src,例如@/components表示src/components目录,assets表示src/assets前缀,如果是assets/img就表示src/assets/img目录。