vue3微信公众号商城项目实战系列(7)自定义底部tabbar组件
在开始之前,先看看官方对组件的定义:
vue3的生态非常丰富,有各种各样的开源组件库可以拿来就用,比如vant、element-ui等,本系列不使用任何第3方组件,
完全使用原生的语法来写,只为聚焦vue3技术本身,本篇写一个自定义tabbar组件,效果如下图所示:
该组件实现如下功能:
1. 底部tab项固定3个:首页、购物车、我的;
2. 购物车 tab 项加数字角标,显示购物车中商品的数量 ,如下图,当在首页添加商品时数量要同步更改。
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书写脚本(有setup标记),各行作用如下:
第2行:导入vue中的函数,reactive 是获取响应式对象的,onMounted 是生命周期钩子函数,在页面加载完 DOM 对象后触发,
当点击 tab 项跳转页面时需要在这个钩子函数中将选中的项置为选中的状态,见第 16~24 行的代码。
第3-7行:导入路由对象,当用户点 tab 项时做页面跳转。
第9-13行:定义响应式数组对象,一个对象只要用 reactive( ) 函数处理一下就可以做双向数据绑定,这就是响应式编程的优势之一。
布局块中的 tab 项的内容就是用这个数据对象输出的。
第15行:用编译宏命令defineProps( ) 给这个组件定义一个 name 属性,这样引用的页面就可以在这个组件上给name属性赋值
,props 对象就可以取到这个值,第19 行的 if 语句就是基于 这个属性值来决定哪个 tab 项用彩色图片和文字来显示的。
样式块代码:
<style> .tab-bar{ width: 100%; position: fixed; left: 0; bottom: 0; padding: 2px 0; border-top: solid 1px #c0c0c0; display: flex; flex-direction: row; justify-content: space-around; } .tab-bar-item{ display: flex; flex-direction: column; } .tab-bar-item1{ display: flex; flex-direction: column; color: #0ab2ac; } .tab-bar-item-img{ width: 28px; height: 28px; } </style>
样式中 .tab-bar 要将 tabbar 绝对定位在页面底部 , 通过 "position: fixed; left: 0; bottom: 0;" 来实现,
显示类型 为 "display: flex; " 可以很方便的给3个 tab 项 横向均匀对齐。其他的就是 css 的基本用法了。
最后,看一个 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'])
,取值的时候用 props.name、props.title、props.text 就可以获取到引用页面设置的属性值了 。
以上就是一个简单的自定义组件的用法,这里还有一个功能 " 购物车加数字角标 " 没有实现
,留着写加购物车功能的时候再完成更合适一些。