Criação e Encapsulamento de Componentes de Barra de Abas no Vue.js

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>
   

Tags: Vue.js componentes Barra de Navegação encapsulamento webpack

Publicado em 10-20 12:22