这个案例结合了前面介绍的slot插槽、vue-router前端路由等知识点,最重要的是体现了组件封装的思想。
配套可执行代码示例 => GitHub
这个案例要封装的就是components/下面的一个公共的Tabbar组件,因为可能在多个页面都需要导航栏。
导航栏的最小元素。
选中时的图标、未选中时的图标、文字,都使用slot具名插槽的形式,提高复用性。
导航栏元素对应的路径path、选中时文字的样式activeColor,都使用props属性的形式,让使用者传入,也是为了提高复用性。
根据当前活跃的路由里是否包含props传进来的path,判断当前
绑定点击事件,用代码跳转路由到该导航栏元素对应的页面组件。
注: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>
为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>
到了MainTabBar这一层,已经是业务相关的了。最外层使用
<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>
在根组件中使用
<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>
配置页面组件和路径的对应关系。
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'),
}
}
}