基于CLI学完后所做的第一个较大的vue案例
0.思路
- 在App.vue中搭建大致的底部栏样式
- 拆解底部栏(大框+切换选项)
- 创建路由
- 实现动态切换路由(v-on)
- 实现动态切换样式(v-bind、props、computed、$route、$router)
- 整合底部栏并抽离出一个完整的底部栏组件,并在app.vue导入该完整的底部栏组件
1.结构搭建
首先,就是在app.vue里面将大致结构搭建出来
template代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div class="tab-bar"> <div class="tab-item"> <img src="../../assets/image/tabbar/home.svg" alt="" slot="image"> <img src="../../assets/image/tabbar/home_active.svg" alt="" slot="img-active"> <div slot="item">首页</div> </div> <div class="tab-item"> <img src="../../assets/image/tabbar/home.svg" alt="" slot="image"> <img src="../../assets/image/tabbar/home_active.svg" alt="" slot="img-active"> <div slot="item">首页</div> </div> <div class="tab-item"> <img src="../../assets/image/tabbar/home.svg" alt="" slot="image"> <img src="../../assets/image/tabbar/home_active.svg" alt="" slot="img-active"> <div slot="item">首页</div> </div> <div class="tab-item"> <img src="../../assets/image/tabbar/home.svg" alt="" slot="image"> <img src="../../assets/image/tabbar/home_active.svg" alt="" slot="img-active"> <div slot="item">首页</div> </div> </div>
|
style代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <style> .tab-bar { display: flex; position: fixed; bottom: 0; left: 0; right: 0; background-color: #f6f6f6; }
.tab-item { flex: 1; height: 49px; text-align: center; }
.tab-item img { width: 24px; height: 24px; margin-top: 3px; vertical-align: middle; margin-bottom: 2px; } </style>
|
2.根据代码进行模块拆解
在components文件夹下新建一个Tabbar.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 class="tab-bar"> <tab-bar-item> <img slot="img" src="../../assets/image/tabbar/home.svg" alt=""> <div slot="item">首页</div> </tab-bar-item> <tab-bar-item> <img slot="img" src="../../assets/image/tabbar/category.svg" alt=""> <div slot="item">分类</div> </tab-bar-item> <tab-bar-item> <img slot="img" src="../../assets/image/tabbar/shopcart.svg" alt=""> <div slot="item">购物车</div> </tab-bar-item> <tab-bar-item> <img slot="img" src="../../assets/image/tabbar/profile.svg" alt=""> <div slot="item">我的</div> </tab-bar-item> </div> </template>
<script> import TabbarItem from "./TabbarItem.vue" export default { name: "Tabbar", components: { TabbarItem } } </script>
<style scoped> .tabbar { display: flex; position: fixed; bottom: 0; left: 0; right: 0; background-color: #f6f6f6; } </style>
|
在该文件夹下再新建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-item"> <slot name="image"></slot> <slot name="item"></slot> </div> </template>
<script> export default { name: "TabbarItem" } </script>
<style scoped> .tab-item { flex: 1; height: 49px; text-align: center; } .tab-item img { width: 24px; height: 24px; margin-top: 3px; vertical-align: middle; margin-bottom: 2px; } </style>
|
3.创建路由,构建相关路由页面
路由代码
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
| import Vue from 'vue' import VueRouter from 'vue-router'
const Home = () => import('../views/Home/Home.vue') const Category = () => import('../views/Category/Category.vue') const Profile = () => import('../views/Profile/Proile.vue') const Shopcart = () => import('../views/Shopcart/Shopcart.vue')
Vue.use(VueRouter)
const routes = [ { path: '', redirect: '/home', }, { path: '/home', name: 'home', component: Home }, { path: '/category', name: 'category', component: Category }, { path: '/shopcart', name: 'shopcart', component: Shopcart }, { path: '/profile', name: 'profile', component: Profile }]
const router = new VueRouter({ routes, mode: 'history'
})
export default router
|
相关路由页面目录
4.动态切换路由
跳回到TabbarItem.vue文件,为其动态绑定一个点击事件change
1 2 3
| <div class="tab-item" @click="change"> </div>
|
实现具体的路由跳转操作
1 2 3 4 5 6 7 8 9 10
| <script> export default { name: "TabbarItem", methods: { change() { this.$router.push(this.path) } } } </script>
|
到这一步,你会发现是不能完成路由跳转的,因为我们需要动态获取到用户点击的是哪个按钮,我们需要动态的获取父级的按钮选项中要跳转的页面,所以我们到它的父级页面去定义一个path属性,同时赋上各自的路由地址
Tabbar.vue代码(path=”地址”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <div class="tab-bar"> <tab-bar-item path="/home" > <img slot="img" src="../../assets/image/tabbar/home.svg" alt=""> <img slot="img-active" src="../../assets/image/tabbar/home_active.svg" alt=""> <div slot="item">首页</div> </tab-bar-item> <tab-bar-item path="/category"> <img slot="img" src="../../assets/image/tabbar/category.svg" alt=""> <img slot="img-active" src="../../assets/image/tabbar/category_active.svg" alt=""> <div slot="item">分类</div> </tab-bar-item> <tab-bar-item path="/shopcart"> <img slot="img" src="../../assets/image/tabbar/shopcart.svg" alt=""> <img slot="img-active" src="../../assets/image/tabbar/shopcart_active.svg" alt=""> <div slot="item">购物车</div> </tab-bar-item> <tab-bar-item path="/profile"> <img slot="img" src="../../assets/image/tabbar/profile.svg" alt=""> <img slot="img-active" src="../../assets/image/tabbar/profile_active.svg" alt=""> <div slot="item">我的</div> </tab-bar-item> </div> </template>
|
TabbarItem定义props属性去接收这些变量
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> export default { name: 'TabBarItem', props:{ path: String, }, methods: { change() { this.$router.push(this.path) } } } </script>
|
5.切换选项样式改变
接着,为了实现点击哪个选项,哪个选项的图片以及字体颜色就会相应的改变,我们需要给TabbarItem一个判断语句,用来判断该组件对应的路由当前是否处于活跃状态,首先为图片定义if-else语句,对其进行判断,若活跃,则显示有颜色的图片,同时,定义一个计算属性-isActive,进行语句判断,若当前活跃的页面就是该组件路由,则显示活跃的图片样式—核心代码( this.$route.path.indexOf(this.path) !== -1
)
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
| <template> <div class="tab-item" @click="change"> <div v-if="!isActive"> <slot name="img"></slot> </div> <div v-else> <slot name="img-active"></slot> </div> <slot name='item'></slot> </div> </template>
<script> export default { name: 'TabBarItem', props:{ path: String, }, methods: { change() { this.$router.push(this.path) } } computed: { isActive() { return this.$route.path.indexOf(this.path) !== -1 } } } </script>
<style scoped> .tab-item { flex: 1; height: 49px; text-align: center; } .tab-item img { width: 24px; height: 24px; margin-top: 3px; /* 去除图片底部内容 */ vertical-align: middle; margin-bottom: 2px; } /* .isred { color: red } */ </style>
|
然后,动态绑定style,同样使用isActive计算属性进行判断
为字体动态绑定样式
1 2 3
| <div :style="activeStyle"> <slot name='item'></slot> </div>
|
方法具体实现
1 2 3 4 5
| computed: { activeStyle() { return this.isActive ? {color: red} :{} } }
|
如果想为其自定义颜色,可以为其定义一个可以动态获取它父组件的属性
1 2 3 4 5 6 7 8
| props:{ path: String, activeColor: { type: String, default: 'red' } }
|
然后你就可以自定义你想要的颜色啦
在其父组件下自定义颜色
1 2 3 4 5
| <tab-bar-item path="/home" activeColor="blue" > <img slot="img" src="../../assets/image/tabbar/home.svg" alt=""> <img slot="img-active" src="../../assets/image/tabbar/home_active.svg" alt=""> <div slot="item">首页</div> </tab-bar-item>
|
6.组件抽离,插槽安排
然后就是将Tabbar.vue进行抽离,定义一个插槽供插入的选项按钮,然后再新建一个MainTabbar文件用来组装这些组件,最后再在App.vue中引用该组件
最后的代码
Tabbar.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 class="tab-bar"> <slot></slot> </div> </template> <script> export default { name: 'TarBar' } </script> <style scoped> .tab-bar { display: flex;
position: fixed; bottom: 0; left: 0; right: 0; background-color: #C7C7C7; box-shadow: 0, -1px, 1px, rgba(216, 214, 214, 0.1) }
</style>
|
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 58 59
| <template> <div class="tab-item" @click="change"> <div v-if="!isActive"> <slot name="img"></slot> </div> <div v-else> <slot name="img-active"></slot> </div> <div :style="activeStyle"> <slot name='item'></slot> </div> </div> </template>
<script> export default { name: 'TabBarItem', props:{ path: String, activeColor: { type: String, default: 'red' } }, methods: { change() { this.$router.push(this.path) } }, data() { return { } }, computed: { isActive() { return this.$route.path.indexOf(this.path) !== -1 }, activeStyle() { return this.isActive ? {color: this.activeColor} :{} } } } </script>
<style scoped> .tab-item { flex: 1; height: 49px; text-align: center; } .tab-item img { width: 24px; height: 24px; margin-top: 3px; /* 去除图片底部内容 */ vertical-align: middle; margin-bottom: 2px; } </style>
|
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
| <template> <div> <tab-bar> <tab-bar-item path="/home" > <img slot="img" src="../../assets/image/tabbar/home.svg" alt=""> <img slot="img-active" src="../../assets/image/tabbar/home_active.svg" alt=""> <div slot="item">首页</div> </tab-bar-item> <tab-bar-item path="/category"> <img slot="img" src="../../assets/image/tabbar/category.svg" alt=""> <img slot="img-active" src="../../assets/image/tabbar/category_active.svg" alt=""> <div slot="item">分类</div> </tab-bar-item> <tab-bar-item path="/shopcart"> <img slot="img" src="../../assets/image/tabbar/shopcart.svg" alt=""> <img slot="img-active" src="../../assets/image/tabbar/shopcart_active.svg" alt=""> <div slot="item">购物车</div> </tab-bar-item> <tab-bar-item path="/profile"> <img slot="img" src="../../assets/image/tabbar/profile.svg" alt=""> <img slot="img-active" src="../../assets/image/tabbar/profile_active.svg" alt=""> <div slot="item">我的</div> </tab-bar-item> </tab-bar> </div> </template>
<script> import TabBar from "../tabbar/TabBar.vue" import TabBarItem from "../tabbar/TabBarItem.vue" export default { name: 'MainTabBar', components: { TabBar, TabBarItem } } </script>
<style scoped> </style>
|
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div id="app"> <router-view></router-view> <main-tab-bar></main-tab-bar> </div> </template>
<script> import MainTabBar from "./components/maintabbar/MainTabBar.vue" export default { name: 'App', components: { MainTabBar } } </script>
<style> /* css样式代码引用 */ @import "./assets/css/base.css"; </style>
|