Método de Encapsulamento 1
Estrutura de Arquivos
A organização dos arquivos é fundamental para um projeto Vue.js bem estruturado. Abaixo, apresentamos a estrutura utilizada neste método:
src/
├── App.vue
├── main.js
├── router/
│ └── index.js
├── assets/
│ └── img/
│ └── tabbar/
│ ├── category.svg
│ ├── category_active.svg
│ ├── home.svg
│ ├── home_active.svg
│ ├── profile.svg
│ ├── profile_active.svg
│ ├── shopcart.svg
│ └── shopcart_active.svg
├── components/
│ └── tabbar/
│ ├── TabBar.vue
│ └── TabBarItem.vue
└── views/
├── Home.vue
├── Category.vue
├── ShopCart.vue
└── Profile.vue
Código dos Componentes
src/App.vue
O componente principal da aplicação que define a estrutura geral e entegra os componentes da barra de navegação.
<template>
<div id="app">
<router-view></router-view>
<tab-bar>
<tab-bar-item path="/home" activeColor="#f87171">
<img slot="item-icon" src="~assets/img/tabbar/home.svg" alt="Ícone da página inicial">
<img slot="item-icon-active" src="~assets/img/tabbar/home_active.svg" alt="Ícone ativo da página inicial">
<div slot="item-text">Início</div>
</tab-bar-item>
<tab-bar-item path="/category" activeColor="deeppink">
<img slot="item-icon" src="~assets/img/tabbar/category.svg" alt="Ícone da categoria">
<img slot="item-icon-active" src="~assets/img/tabbar/category_active.svg" alt="Ícone ativo da categoria">
<div slot="item-text">Categorias</div>
</tab-bar-item>
<tab-bar-item path="/shopcart">
<img slot="item-icon" src="~assets/img/tabbar/shopcart.svg" alt="Ícone do carrinho">
<img slot="item-icon-active" src="~assets/img/tabbar/shopcart_active.svg" alt="Ícone ativo do carrinho">
<div slot="item-text">Carrinho</div>
</tab-bar-item>
<tab-bar-item path="/profile">
<img slot="item-icon" src="~assets/img/tabbar/profile.svg" alt="Ícone do perfil">
<img slot="item-icon-active" src="~assets/img/tabbar/profile_active.svg" alt="Ícone ativo do perfil">
<div slot="item-text">Perfil</div>
</tab-bar-item>
</tab-bar>
</div>
</template>
<script>
import TabBar from "./components/tabbar/TabBar.vue";
import TabBarItem from "./components/tabbar/TabBarItem.vue";
export default {
name: 'App',
components: {
TabBar,
TabBarItem
}
}
</script>
<style>
@import "~assets/css/base.css";
</style>
src/components/tabbar/TabBarItem.vue
Este componente representa um item individual na barra de navegação. Ele gerencia o estado ativo, o ícone e o texto, além de lidar com a navegação.
<template>
<div class="tab-bar-item" @click="navigateTo">
<div v-if="!isActive">
<slot name="item-icon"></slot>
</div>
<div v-else>
<slot name="item-icon-active"></slot>
</div>
<div :style="activeTextStyle">
<slot name="item-text"></slot>
</div>
</div>
</template>
<script>
export default {
name: "TabBarItem",
props: {
path: {
type: String,
required: true
},
activeColor: {
type: String,
default: '#10B981' // Cor padrão do Tailwind CSS Green-500
}
},
computed: {
isActive() {
// Verifica se o caminho atual da rota contém o caminho deste item
return this.$route.path.includes(this.path);
},
activeTextStyle() {
// Define a cor do texto com base no estado ativo e na cor definida
return this.isActive ? { color: this.activeColor } : {};
}
},
methods: {
navigateTo() {
// Navega para o caminho especificado, evitando reentrada se já estiver no caminho
if (this.$route.path !== this.path) {
this.$router.replace(this.path).catch(err => {
// Captura e ignora erros de navegação duplicada
if (err.name !== 'NavigationDuplicated') {
console.error("Erro de navegação:", err);
}
});
}
}
}
}
</script>
<style scoped>
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.tab-bar-item img {
width: 24px;
height: 24px;
vertical-align: middle;
}
</style>
src/components/tabbar/TabBar.vue
O componente TabBar atua como um contêiner flexível para os TabBarItem, definindo a aparência e o layout fixo da barra de navegação inferior.
<template>
<div id="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name: "TabBar"
}
</script>
<style scoped>
#tab-bar {
display: flex;
background-color: #f7f7f7; /* Cor de fundo suave */
position: fixed;
left: 0;
right: 0;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.1); /* Sombra sutil superior */
z-index: 10; /* Garante que a barra fique sobre outros conteúdos */
}
</style>
Método de Encapsulamento 2 (Recomendado)
Este método introduz o uso de aliases de caminho no Webpack para simplificar a importação de recursos e centraliza a definição da barra de navegação em um componente dedicado.
Estrutura de Arquivos
A configuração do Webpack com aliases melhora a legibilidade e a manutenção do código.
src/
├── App.vue
├── main.js
├── router/
│ └── index.js
├── assets/
│ └── img/
│ └── tabbar/
│ ├── category.svg
│ ├── category_active.svg
│ ├── home.svg
│ ├── home_active.svg
│ ├── profile.svg
│ ├── profile_active.svg
│ ├── shopcart.svg
│ └── shopcart_active.svg
├── components/
│ ├── MainTabBar.vue <-- Novo componente centralizado
│ └── tabbar/
│ ├── TabBar.vue
│ └── TabBarItem.vue
└── views/
├── Home.vue
├── Category.vue
├── ShopCart.vue
└── Profile.vue
Configuração do Webpack
build/webpack.base.conf.js
Adição de aliases para faciliatr a importação de diretórios comuns como src, assets, components e views.
// ... outras configurações do webpack
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'assets': resolve('src/assets'),
'components': resolve('src/components'),
'views': resolve('src/views')
}
},
// ...
Código dos Componentes
src/components/MainTabBar.vue
Este componente encapsula toda a lógica e a estrutura da barra de navegação principal, simplificando o App.vue.
<template>
<tab-bar>
<tab-bar-item path="/home" activeColor="#f87171">
<img slot="item-icon" src="~assets/img/tabbar/home.svg" alt="Ícone da página inicial">
<img slot="item-icon-active" src="~assets/img/tabbar/home_active.svg" alt="Ícone ativo da página inicial">
<div slot="item-text">Início</div>
</tab-bar-item>
<tab-bar-item path="/category" activeColor="deeppink">
<img slot="item-icon" src="~assets/img/tabbar/category.svg" alt="Ícone da categoria">
<img slot="item-icon-active" src="~assets/img/tabbar/category_active.svg" alt="Ícone ativo da categoria">
<div slot="item-text">Categorias</div>
</tab-bar-item>
<tab-bar-item path="/shopcart">
<img slot="item-icon" src="~assets/img/tabbar/shopcart.svg" alt="Ícone do carrinho">
<img slot="item-icon-active" src="~assets/img/tabbar/shopcart_active.svg" alt="Ícone ativo do carrinho">
<div slot="item-text">Carrinho</div>
</tab-bar-item>
<tab-bar-item path="/profile">
<img slot="item-icon" src="~assets/img/tabbar/profile.svg" alt="Ícone do perfil">
<img slot="item-icon-active" src="~assets/img/tabbar/profile_active.svg" alt="Ícone ativo do perfil">
<div slot="item-text">Perfil</div>
</tab-bar-item>
</tab-bar>
</template>
<script>
import TabBar from "@/components/tabbar/TabBar.vue";
import TabBarItem from "@/components/tabbar/TabBarItem.vue"; // Ajustado para usar o alias
export default {
name: "MainTabBar",
components: {
TabBar,
TabBarItem
}
}
</script>
<style scoped>
/* Estilos específicos para o MainTabBar, se houver */
</style>
src/App.vue
O App.vue torna-se mais limpo, apenas renderizando a rota principle e o MainTabBar.
<template>
<div id="app">
<router-view></router-view>
<main-tab-bar></main-tab-bar>
</div>
</template>
<script>
import MainTabBar from "./components/MainTabBar"; // Importa o componente centralizado
export default {
name: 'App',
components: {
MainTabBar
}
}
</script>
<style>
@import "~assets/css/base.css";
</style>