如何在微信原生小程序中使用van-tabbar 实现:自定义底部导航栏
생성일: 2025년 2월 17일
생성일: 2025년 2월 17일
如何在微信原生小程序中使用van-tabbar 实现:自定义底部导航栏
下面给出一个在微信原生小程序中使用 Vant Weapp 的 van-tabbar 组件,自定义底部导航栏的示例步骤。假设你已经能够在项目中正常安装和使用 Vant Weapp,如果没有,可以参考下面的简要安装步骤。
bashnpm init -y npm install @vant/weapp --save
mini.project.json
文件:
json{ "setting": { "packNpmManually": true, "packNpmRelationList": [ { "packageJsonPath": "./package.json", "miniprogramNpmDistDir": "./miniprogram_npm" } ] } }
如果不方便使用 npm,可以直接下载 Vant Weapp 仓库,然后将 dist
目录拷贝到小程序目录中。例如放到 components/vant-weapp
下面,然后使用相对路径引用。
如果你想完全自定义底部导航栏,需要在 app.json
里去掉/注释掉原生 tabBar 的相关配置。一般情况下,不配置 tabBar
即可。如果你已经配置过,需要删除或者改成自定义组件的方式。
你可以将自定义的底部导航写在每个页面的 WXML 中,也可以封装成一个公共的自定义组件,然后在各页面中直接引用。这里演示的是最简形式,直接写在某个页面里(比如 pages/index/index
):
假设你是通过 npm 的方式安装,并且使用自动构建,这时引入路径一般是:
json{ "usingComponents": { "van-tabbar": "@vant/weapp/tabbar/index", "van-tabbar-item": "@vant/weapp/tabbar-item/index" } }
如果你是直接复制的 dist
文件夹,则引入路径参考你放置的真实位置,示例:
json{ "usingComponents": { "van-tabbar": "/components/vant-weapp/tabbar/index", "van-tabbar-item": "/components/vant-weapp/tabbar-item/index" } }
html<!-- pages/index/index.wxml --> <view class="container"> <!-- 其他内容 --> <van-tabbar active="{{ active }}" bind:change="onChange" safe-area-inset-bottom > <van-tabbar-item icon="home-o" name="home">首页</van-tabbar-item> <van-tabbar-item icon="search" name="search">搜索</van-tabbar-item> <van-tabbar-item icon="friends-o" name="friends">好友</van-tabbar-item> <van-tabbar-item icon="setting-o" name="settings">设置</van-tabbar-item> </van-tabbar> </view>
说明:
active="{{ active }}"
用于控制当前选中的 tab。bind:change="onChange"
是当用户点击切换 tab 时的事件回调。safe-area-inset-bottom
属性可以兼容 iPhone X 系列等带有底部安全区域的设备,自动给底部留出安全区域。
js// pages/index/index.js Page({ data: { active: 'home' // 默认选中的 tab }, onChange(event) { // event.detail 即当前点击的 tabbar-item 的 name const currentName = event.detail; this.setData({ active: currentName }); // 这里可以做页面跳转 switch (currentName) { case 'home': wx.switchTab({ url: '/pages/home/home' }); break; case 'search': wx.navigateTo({ url: '/pages/search/search' }); break; case 'friends': wx.navigateTo({ url: '/pages/friends/friends' }); break; case 'settings': wx.navigateTo({ url: '/pages/settings/settings' }); break; default: break; } } });
注意:
- 如果你要用原生的
tabBar
类似switchTab
跳转,需要先在app.json
的pages
里注册对应页面,否则会报错。- 若你是完全自定义导航,可以使用
navigateTo
、redirectTo
等自定义页面跳转方式。
如果项目里多页面都需要相同的底部导航,可以把 van-tabbar 的使用封装到一个自定义组件里,比如 components/custom-tabbar/custom-tabbar
,再在各个页面中引入这个自定义组件。这样便于统一管理。例如:
json// components/custom-tabbar/custom-tabbar.json { "component": true, "usingComponents": { "van-tabbar": "@vant/weapp/tabbar/index", "van-tabbar-item": "@vant/weapp/tabbar-item/index" } }
html<!-- components/custom-tabbar/custom-tabbar.wxml --> <van-tabbar active="{{ active }}" bind:change="onChange" safe-area-inset-bottom > <van-tabbar-item icon="home-o" name="home">首页</van-tabbar-item> <van-tabbar-item icon="search" name="search">搜索</van-tabbar-item> <van-tabbar-item icon="friends-o" name="friends">好友</van-tabbar-item> <van-tabbar-item icon="setting-o" name="settings">设置</van-tabbar-item> </van-tabbar>
js// components/custom-tabbar/custom-tabbar.js Component({ properties: { active: { type: String, value: 'home' } }, methods: { onChange(event) { const currentName = event.detail; this.triggerEvent('change', { value: currentName }); } } });
然后在你的页面中使用:
json// pages/index/index.json { "usingComponents": { "custom-tabbar": "/components/custom-tabbar/custom-tabbar" } }
html<!-- pages/index/index.wxml --> <view class="container"> <!-- 其他内容 --> <custom-tabbar active="{{ active }}" bind:change="onTabbarChange" /> </view>
js// pages/index/index.js Page({ data: { active: 'home' }, onTabbarChange(e) { const currentName = e.detail.value; this.setData({ active: currentName }); // 这里做页面跳转等逻辑 // ... } });
这样就可以实现多页面共享同一个底部导航栏的逻辑。
app.json
中配置原生 tabBar
。active
属性控制当前选中的标签,在 bind:change
中做页面跳转或逻辑处理。通过以上步骤,就可以在微信原生小程序中使用 Vant Weapp 的 van-tabbar 实现自定义底部导航栏。这样可以自由定制图标、文字、交互逻辑等,也可以结合小程序本身的页面路由进行跳转。希望能帮你快速上手!
需求完善:
1.导航栏 tab数量:固定是4个
2.每个tab 显示名称和 url和icon不同
3.用函数方式实现控制:入参:lx(0/经销商 1/甲方 2/护工 3/用户)
函数放置在app.js中
<van-tabbar
active="{{ active }}"
bind:change="onChange"
safe-area-inset-bottom>
<van-tabbar-item icon="home-o" name="home">首页</van-tabbar-item>
<van-tabbar-item icon="search" name="search">搜索</van-tabbar-item>
<van-tabbar-item icon="friends-o" name="friends">好友</van-tabbar-item>
<van-tabbar-item icon="setting-o" name="settings">设置</van-tabbar-item>
</van-tabbar>
下面演示一个思路:
app.js
中编写一个函数,根据不同用户类型(0/1/2/3)返回对应的 4 个 Tab 配置(文本、图标、跳转 URL 等)。getApp()
调用这个函数获得 Tab 配置,使用 van-tabbar
+ van-tabbar-item
动态渲染。bind:change
回调里,根据选中的 tab 执行不同的跳转逻辑。下面给出一个完整的示例供你参考。
在小程序根目录下的 app.js
中,添加一个方法 getTabbarConfig(lx)
,用来返回对应角色(lx)下的 4 个 Tab 数据。
js// app.js App({ onLaunch() { // ... }, /** * 根据不同用户类型,返回对应的 Tab 配置 * lx: 0(经销商), 1(甲方), 2(护工), 3(用户) */ getTabbarConfig(lx) { switch (lx) { case 0: // 经销商 return [ { name: 'home', text: '首页', icon: 'home-o', url: '/pages/dealer/home/home' }, { name: 'order', text: '订单', icon: 'orders-o', url: '/pages/dealer/order/order' }, { name: 'friends', text: '好友', icon: 'friends-o', url: '/pages/dealer/friends/friends' }, { name: 'profile', text: '我的', icon: 'setting-o', url: '/pages/dealer/profile/profile' }, ]; case 1: // 甲方 return [ { name: 'home', text: '首页', icon: 'home-o', url: '/pages/client/home/home' }, { name: 'bill', text: '账单', icon: 'bill-o', url: '/pages/client/bill/bill' }, { name: 'search', text: '搜索', icon: 'search', url: '/pages/client/search/search' }, { name: 'profile', text: '我的', icon: 'setting-o', url: '/pages/client/profile/profile' }, ]; case 2: // 护工 return [ { name: 'home', text: '首页', icon: 'home-o', url: '/pages/nurse/home/home' }, { name: 'task', text: '任务', icon: 'todo-list-o', url: '/pages/nurse/task/task' }, { name: 'friends', text: '好友', icon: 'friends-o', url: '/pages/nurse/friends/friends' }, { name: 'profile', text: '我的', icon: 'setting-o', url: '/pages/nurse/profile/profile' }, ]; case 3: // 普通用户 return [ { name: 'home', text: '首页', icon: 'home-o', url: '/pages/user/home/home' }, { name: 'search', text: '发现', icon: 'search', url: '/pages/user/discover/discover' }, { name: 'cart', text: '购物车', icon: 'shopping-cart-o', url: '/pages/user/cart/cart' }, { name: 'profile', text: '个人', icon: 'setting-o', url: '/pages/user/profile/profile' }, ]; default: // 其他或默认 return [ { name: 'home', text: '首页', icon: 'home-o', url: '/pages/home/home' }, { name: 'search', text: '搜索', icon: 'search', url: '/pages/search/search' }, { name: 'friends', text: '好友', icon: 'friends-o', url: '/pages/friends/friends' }, { name: 'settings', text: '设置', icon: 'setting-o', url: '/pages/settings/settings' }, ]; } } });
- 你可以根据自己的路由结构修改
url
。icon
可以用 Vant Weapp 内置的 icon 名称(如home-o
、search
、friends-o
、setting-o
等),也可以用自定义图标。
下面以某个页面(比如 pages/index/index
)为例,说明如何动态渲染并响应点击事件。
先在 pages/index/index.json
中引入 Vant Weapp 组件:
json{ "usingComponents": { "van-tabbar": "@vant/weapp/tabbar/index", "van-tabbar-item": "@vant/weapp/tabbar-item/index" } }
如果你是通过拷贝
dist
方式引入,需要把路径改成相对路径:json{ "usingComponents": { "van-tabbar": "/components/vant-weapp/tabbar/index", "van-tabbar-item": "/components/vant-weapp/tabbar-item/index" } }
示例 pages/index/index.wxml
:
html<view class="container"> <!-- 其他页面内容 --> <!-- 自定义底部导航栏 --> <van-tabbar active="{{ activeIndex }}" bind:change="onChange" safe-area-inset-bottom > <block wx:for="{{ tabList }}" wx:key="name"> <!-- 使用 tab 的 icon、text、name 动态渲染 --> <van-tabbar-item icon="{{ item.icon }}" name="{{ item.name }}"> {{ item.text }} </van-tabbar-item> </block> </van-tabbar> </view>
active="{{ activeIndex }}"
表示当前选中的 tab 索引或名称。这里我们演示用“名称(name)”来区分。- 通过
wx:for
遍历在data.tabList
中的 4 个 tab 配置,动态生成van-tabbar-item
。
示例 pages/index/index.js
:
jsconst app = getApp(); Page({ data: { tabList: [], // 存放4个Tab的数组 activeIndex: '', // 当前激活的Tab名称 userType: 0 // 假设用来存储用户类型(0=经销商,1=甲方,2=护工,3=用户) }, onLoad(options) { // 也可以从 options 或 app.globalData 中获取用户类型 // 这里演示写死 userType = 0 (经销商),实际根据业务修改 const userType = 0; // 或者: const userType = app.globalData.userType; // 调用 app.js 提供的函数,获取该角色的底部导航配置 const tabs = app.getTabbarConfig(userType); // 设置 data this.setData({ userType, tabList: tabs, activeIndex: tabs.length ? tabs[0].name : '' // 默认选中第1个 }); }, // 底部Tab切换时的回调 onChange(event) { // event.detail 即 van-tabbar 传递的 'name' 值 const clickedName = event.detail; this.setData({ activeIndex: clickedName }); // 找到对应的 tab 配置,拿到要跳转的页面 const tabItem = this.data.tabList.find(item => item.name === clickedName); if (tabItem) { // 路由跳转 // 如果这些目标页面是小程序的“tabBar”页面,需要用 wx.switchTab // 否则用 navigateTo / redirectTo 均可 wx.navigateTo({ url: tabItem.url }); } } });
注意:
- 如果你想使用
switchTab
进行跳转,则需要在小程序app.json
中的tabBar -> list
里提前声明那些页面。否则会提示“页面未定义在 tabBar 中”。- 如果你是完全自定义 tabBar,不想用微信原生的
tabBar
配置,就可以使用navigateTo
、redirectTo
等常规跳转 API。- 代码中的 “默认选中第1个” 只是一种示例,你可以根据实际需要来初始化
activeIndex
。
如果多个页面都需要同样的逻辑,你可以把底部导航封装成一个自定义组件(如 components/custom-tabbar/custom-tabbar
),并在组件内写上 “根据用户类型获取 tabList 并渲染” 的逻辑,再在各个页面引用这个组件。
示例结构:
textcomponents/ └─ custom-tabbar/ ├─ custom-tabbar.js ├─ custom-tabbar.json ├─ custom-tabbar.wxml └─ custom-tabbar.wxss
custom-tabbar.json:
json{ "component": true, "usingComponents": { "van-tabbar": "@vant/weapp/tabbar/index", "van-tabbar-item": "@vant/weapp/tabbar-item/index" } }
custom-tabbar.wxml:
html<van-tabbar active="{{ activeIndex }}" bind:change="onChange" safe-area-inset-bottom > <block wx:for="{{ tabList }}" wx:key="name"> <van-tabbar-item icon="{{ item.icon }}" name="{{ item.name }}"> {{ item.text }} </van-tabbar-item> </block> </van-tabbar>
custom-tabbar.js:
jsconst app = getApp(); Component({ properties: { // 父级可以传入用户类型,也可以在组件里自己去获取 userType: { type: Number, value: 0 } }, data: { tabList: [], activeIndex: '' }, lifetimes: { attached() { // 根据 userType 获取对应4个Tab const tabs = app.getTabbarConfig(this.data.userType); this.setData({ tabList: tabs, activeIndex: tabs.length ? tabs[0].name : '' }); } }, methods: { onChange(event) { const clickedName = event.detail; this.setData({ activeIndex: clickedName }); const tabItem = this.data.tabList.find(item => item.name === clickedName); if (tabItem) { wx.navigateTo({ url: tabItem.url }); } } } });
然后在页面中使用这个自定义组件:
pages/index/index.json:
json{ "usingComponents": { "custom-tabbar": "/components/custom-tabbar/custom-tabbar" } }
pages/index/index.wxml:
html<view class="container"> <!-- 其他内容 --> <!-- 使用自定义底部导航 --> <custom-tabbar user-type="{{ 0 }}"/> </view>
这样就不用在每个页面手写 van-tabbar
标签,统一管理比较方便。
app.js
中定义一个“根据用户类型返回 4 个 Tab 配置”的函数,包含文字、图标和跳转 URL 等信息。getApp()
获取 app
实例,调用该函数拿到所需的 4 个 Tab 配置,然后使用 van-tabbar
+ wx:for
动态渲染。bind:change
事件回调中,根据当前点击的 Tab 找到对应的 URL,调用 wx.navigateTo
或 wx.switchTab
实现页面跳转。按照以上步骤,即可实现根据不同用户类型,渲染固定 4 个 Tab,并且每个 Tab 的文字、图标和跳转页面都可以灵活配置的功能。这样就可以满足“经销商、甲方、护工、用户”四种角色,底部导航各不相同的需求。
修改:
tab切换时,如果是当前tab,则:
{{ active }} 输出 1
否则输出 0
<van-tabbar
active="{{ activeIndex }}"
bind:change="tab_onChange"
safe-area-inset-bottom>
<block wx:for="{{ tabList }}" wx:key="name">
<!-- 使用 tab 的 icon、text、name 动态渲染 -->
<van-tabbar-item>
<image
slot="icon-active"
src="/static/footer/{{ item.icon }}{{ active }}.png"
mode="aspectFit"
style="width: 30px; height: 18px;"
/>
{{ item.text }}
</van-tabbar>text</van-tabbar-item> </block>
下面给你演示一种**“在同一个图片文件名里,通过末尾加 0/1 区分是否高亮”**的用法,并配合 Vant Weapp 的 <van-tabbar>
实现“点到当前 Tab 就显示图片名+1.png
,否则显示图片名+0.png
”的需求。
前提: 你已经在页面中正常引入了
van-tabbar
、van-tabbar-item
组件(无论是通过 npm 安装还是拷贝dist
),并且页面 JSON 中有对应的usingComponents
配置。
name
、text
、icon
等属性。同时,新加一个字段 active
,值为 0 或 1,表示“是否高亮”。activeIndex
用于标识当前选中的 tab(通常是它的 name
)。bind:change
事件中,拿到点击的 tab 名称(event.detail
) 后:
activeIndex
tabList
,如果 item.name === activeIndex
就令 item.active=1
,否则置 item.active=0
active=1
,其它 tab 的 active=0
”"/static/footer/" + item.icon + item.active + ".png"
,从而达到“当前 tab 用 xxx1.png
,非当前 tab 用 xxx0.png
”的效果。以下示例假设有四个 Tab:首页(home)、搜索(search)、好友(friends)、设置(settings)
。默认第一个 tab 为选中状态,即它的 active=1
,其余为 active=0
。
pages/index/index.js
)jsPage({ data: { // 四个 tab 的初始数据 tabList: [ { name: 'home', text: '首页', icon: 'home', active: 1 }, // 第一个默认选中 { name: 'search', text: '搜索', icon: 'search', active: 0 }, { name: 'friends', text: '好友', icon: 'friends', active: 0 }, { name: 'settings', text: '设置', icon: 'setting', active: 0 } ], // 当前选中的 Tab 名称 activeIndex: 'home' }, onLoad() { // 你也可以在 onLoad 里去获取后端返回的 tabList 或者做一些逻辑 }, // tab 切换事件 tab_onChange(e) { // Vant Weapp 的 van-tabbar,点击后会把当前选中的 name 传到 event.detail const currentName = e.detail; // 更新 activeIndex this.setData({ activeIndex: currentName }); // 遍历 tabList,如果是当前 name,就令 active=1,否则=0 const newList = this.data.tabList.map(item => { if (item.name === currentName) { item.active = 1; } else { item.active = 0; } return item; }); this.setData({ tabList: newList }); // ★ 这里根据实际需要进行页面跳转 // 比如 if (currentName === 'search') wx.navigateTo({ url: '/pages/search/search' }); // ... } });
pages/index/index.wxml
)html<view class="container"> <!-- 这里是页面的主要内容 --> <!-- 底部自定义导航栏 --> <van-tabbar active="{{ activeIndex }}" bind:change="tab_onChange" safe-area-inset-bottom > <block wx:for="{{ tabList }}" wx:key="name"> <!-- 如果只定义 slot="icon-active",则只有当 tab 处于“选中”状态时才会显示该图片; 若要在“未选中”时也显示图标,推荐给 slot="icon" 或给 van-tabbar-item 传 icon 属性。 这里演示的是只在激活时的图标。 --> <van-tabbar-item name="{{ item.name }}"> <image slot="icon-active" src="/static/footer/{{ item.icon }}{{ item.active }}.png" mode="aspectFit" style="width: 30px; height: 18px;" /> {{ item.text }} </van-tabbar-item> </block> </van-tabbar> </view>
注意:
- 上述做法中,你只定义了
slot="icon-active"
,因此 只在当前 tab(active=1)时显示图标,非当前 tab(active=0)时将没有任何图标。- 如果你希望在“未选中”和“选中”两个状态都有图标,只是文件名不同,那么通常的写法是:
这样就能分别在“未选中”与“选中”时展示两张不同图标。html<van-tabbar-item name="{{ item.name }}"> <!-- slot="icon" 用于未选中状态 --> <image slot="icon" src="/static/footer/{{ item.icon }}0.png" mode="aspectFit" style="width: 30px; height: 18px;" /> <!-- slot="icon-active" 用于选中状态 --> <image slot="icon-active" src="/static/footer/{{ item.icon }}1.png" mode="aspectFit" style="width: 30px; height: 18px;" /> {{ item.text }} </van-tabbar-item>
icon
vs. slot="icon"
<van-tabbar-item>
传递 icon
和 icon-active
这两个属性,也可以使用 slot="icon"
与 slot="icon-active"
。它们功能类似,都是指定“未选中图标”和“选中图标”。html<van-tabbar-item name="{{ item.name }}" icon="/static/footer/{{ item.icon }}0.png" icon-active="/static/footer/{{ item.icon }}1.png" > {{ item.text }} </van-tabbar-item>
html<van-tabbar-item name="{{ item.name }}"> <image slot="icon" src="/static/footer/{{ item.icon }}0.png" /> <image slot="icon-active" src="/static/footer/{{ item.icon }}1.png" /> {{ item.text }} </van-tabbar-item>
tabList
数组里,给每个 tab 携带一个 active
标识(0/1),或在 onChange
时根据当前激活的 name
动态生成想要的图片路径。<van-tabbar-item>
提供的 icon / icon-active
属性或 slot="icon" / slot="icon-active"
,分别指定未选中和选中时的图标。active
值的更新,否则无法正确显示。icon="/static/footer/xxx0.png" icon-active="/static/footer/xxx1.png"
,省去手动判断 “0/1” 的步骤,更加直观。按照上述方式,就可以实现点击某个 Tab 后,若是当前 Tab 则使用 xxx1.png
,否则使用 xxx0.png
的自定义底部导航逻辑了。