vue3微信公众号商城项目实战系列(7)自定义底部tabbar组件

发布时间 2023-04-18 14:40:21作者: 屏风马

在开始之前,先看看官方对组件的定义:

 

 

vue3的生态非常丰富,有各种各样的开源组件库可以拿来就用,比如vant、element-ui等,本系列不使用任何第3方组件,

完全使用原生的语法来写,只为聚焦vue3技术本身,本篇写一个自定义tabbar组件,效果如下图所示:

要实现如下功能:

1. 底部tab项固定3个:首页、购物车、我的;

2. 购物车项加数字角标,显示购物车中商品的数量,当在首页添加商品时数量要同步更改。

3.点击tab项跳转相应页面。

4. 组件的使用尽可能简单,理论上页面引用的时候只要传递一个当前页面的名称就可以了,其他逻辑全部在组件内部完成。

明确了需求,下面从0开始构建一个tabbar组件。

第1步:新建文件和文件夹,如下图:

各文件说明如下:

1. TabBar.vue 是要实现的组件文件,放在 components 目录下,以和页面区分。

2. tabBarIcon 是存放组件使用的图标目录,名称中有数字1的表示选中的 tab 项对应的图标 。

3. 为了处理简洁,我们用约定的方式来处理点击 tab 项时的页面跳转,如下:

Home.vue 是首页,Cart.vue是购物车页,Mine.vue 是我的页(页面是特殊的组件,也可称页面组件),放在 views 目录,

这3个文件都要引用 TabBar.vue 这个自定义组件,根据官方的建议(见下图),组件名称用 PascalCase 的标签名。

页面引用方式如下:

TabBar.vue 代码如下:

布局块代码:

<template>
    <div class="tab-bar">
        <template v-for="item in arrTab">
            <div :class="item.tabClass" @click="onClick(item.tabName)"> 
                <img class="tab-bar-item-img" :src="item.tabIcon" />
                <span>{{ item.tabText }}</span>
            </div> 
        </template> 
    </div>    
</template>

模板块使用 v-for 循环输出各 tab 项,数组 arrTab 是在脚本块定义的 ,动态取值的部分直接绑定对象值。

其中 HTML元素的属性绑定值用 " : ", 属性中的内容用 "{{ }}" 来绑定。

脚本块代码:

 1 <script setup> 
 2 import { reactive, onMounted } from 'vue';
 3 import {useRouter} from 'vue-router';
 4 const router = new useRouter();
 5 function onClick(routerName){
 6     router.push({name:routerName});
 7 }
 8 
 9 const arrTab = reactive([
10     {tabName:'home', tabClass:'tab-bar-item', tabText:'首页', tabIcon:'/src/components/tabBarIcon/home.png'},
11     {tabName:'cart', tabClass:'tab-bar-item', tabText:'购物车', tabIcon:'/src/components/tabBarIcon/cart.png'},
12     {tabName:'mine', tabClass:'tab-bar-item', tabText:'我的', tabIcon:'/src/components/tabBarIcon/mine.png'},
13 ]);
14 
15 const props = defineProps(['name']) 
16 onMounted(() => {
17     console.log(props.name);
18     arrTab.forEach((item,index)=>{
19         if(item.tabName===props.name){
20             item.tabClass="tab-bar-item1";
21             item.tabIcon=item.tabIcon.replace(".png","1.png");
22         }
23     });
24 })
25 </script>

这里使用组合式API书写脚本,各行作用如下:

第2行:导入vue中的函数,reactive 是获取响应式对象的,onMounted 是生命周期钩子函数,在页面加载完 DOM 对象后触发,

当点击 tab 项跳转页面时需要在这个钩子函数中将选中的项置为选中的状态,见第 16~24 行的代码。

第3-7行:导入路由对象,当用户点 tab 项时做页面跳转。

第9-13行:定义响应式数组对象,一个对象只要用 reactive( ) 函数处理一下就可以做双向数据绑定,这就是响应式编程的优势之一。

布局块中的 tab 项的内容就是用这个数据对象输出的。

第15行:给这个组件定义一个属性,这样引用的页面就可以在这个组件上给name属性赋值

,props对象就可以取到这个值,第19 行的 if 语句就是基于 这个属性值来决定哪个 tab 项用彩色图片和文字来显示的。

最后,看一个 Home.vue 、 Cart.vue、Mine.vue 中的代码:

Home.vue 代码:

 

<template>
    <div>
        <span>公众号商城首页</span> 
        <TabBar name="home" />
    </div>    
</template>


<script setup>
import TabBar from '@/components/TabBar.vue';
 
</script>

 

Cart.vue 代码:

<template>
    <div>
        <span>购物车</span> 
        <TabBar name="cart" />
    </div>    
</template>


<script setup>
import TabBar from '@/components/TabBar.vue';
 
</script>

Mine.vue 代码:

<template>
    <div>
        <span>我的</span> 
        <TabBar name="mine" />
    </div>    
</template>


<script setup>
import TabBar from '@/components/TabBar.vue';
 
</script>

这3个文件中  <TabBar name="xxxx" /> 的 name 属性就是对应  const props = defineProps(['name']) 这里的 name 字符串

,defineProps 是一个仅 <script setup> 中可用的编译宏命令,并不需要显式地导入,其传入的参数是一个字符数组

, 如果有多个属性,加到这个数组当中就可以了,形如:const props = defineProps(['name', 'title', 'text'])

 

 

 

以上就是一个简单的自定义组件的用法,这里还有一个功能 " 购物车加数字角标 " 没有实现

,留着写加购物车功能的时候再完成更合适一些。