v1.11.0 发布:全新 TabBar 组件体系与 SCSS 主题定制全面升级
v1.11.0 新增TabBar/TabBarItem自定义底部导航栏组件支持徽标badge / dot、切换拦截器beforeChange、插槽自定义组件样式全面迁移到 SCSS支持 SCSS 变量!default编译时覆盖 CSS 自定义属性运行时覆盖双层级主题定制组件目录按 easycom 规范重构前言meng-xi/uni-router在 v1.10.0 已经提供了完整的路由管理能力但自定义底部导航栏仍需开发者从零实现。uni-app 原生tabBar配置灵活度有限无法支持徽标、切换拦截、插槽自定义等高级需求。v1.11.0 通过新增TabBar/TabBarItem组件填补这一空白同时对组件体系做了 easycom 规范化与 SCSS 主题定制的全面升级。一、问题分析1. 原生 tabBar 的局限uni-app 的tabBar通过pages.json静态配置存在以下限制{tabBar:{list:[{pagePath:pages/index/index,text:首页,iconPath:static/home.png,selectedIconPath:static/home-active.png}]}}无法动态控制图标、文字、显隐均无法运行时切换不支持徽标无法显示未读数、小红点不支持拦截无法在切换前执行权限校验或确认弹窗无法自定义渲染不支持插槽、自定义图标组件样式不可定制高度、颜色、安全区适配等无法精细控制2. 组件目录不符合 easycom 规范v1.10.0 的组件采用扁平文件结构components/ ├── RouterLink.vue ├── TabBar.vue └── TabBarItem.vueuni-app easycom 要求components/name/name.vue嵌套结构才能自动注册。npm 包用户需要手动配置pages.json的easycom.custom规则而 uni_modules 用户则完全无法自动注册。3. 样式无法定制组件内部使用硬编码 CSS 值无法通过变量或自定义属性覆盖主题色、高度、字号等。二、新增能力1. TabBar / TabBarItem 组件基本用法template TabBar selected-color#007aff changeonChange TabBarItem to/pages/index/index icon-path/static/home.png selected-icon-path/static/home-active.png text首页 / TabBarItem to/pages/msg/index icon-path/static/msg.png selected-icon-path/static/msg-active.png text消息 :badge5 / TabBarItem to/pages/user/index icon-path/static/user.png selected-icon-path/static/user-active.png text我的 dot / /TabBar /template script setup import { useRouter } from meng-xi/uni-router const onChange (item, index) { console.log(切换到: ${item.text}, 索引: ${index}) } /script徽标系统TabBarItem内置三种徽标模式优先级dotbadge!-- 小红点最高优先级 -- TabBarItem to/pages/msg/index text消息 dot / !-- 数字徽标超过 badgeMax 显示 {max} -- TabBarItem to/pages/msg/index text消息 :badge99 :badge-max99 / !-- 文字徽标 -- TabBarItem to/pages/msg/index text消息 badgeNew / !-- 自定义徽标颜色 -- TabBarItem to/pages/msg/index text消息 dot badge-color#ff6900 /切换拦截器beforeChange可在切换前执行权限校验或确认弹窗返回false或reject阻止切换TabBar :before-changebeforeChange TabBarItem to/pages/index/index text首页 / TabBarItem to/pages/vip/index text会员 / /TabBar script setup const beforeChange async (item, index) { if (item.to.includes(/vip) !isVip()) { uni.showToast({ title: 请先开通会员, icon: none }) return false } return true } /script插槽自定义TabBar selected-color#007aff !-- 自定义图标 -- TabBarItem to/pages/index/index text首页 template #icon{ active } CustomIcon :nameactive ? home-fill : home / /template /TabBarItem !-- 自定义文字 -- TabBarItem to/pages/user/index icon-path/static/user.png selected-icon-path/static/user-active.png text stylefont-weight: bold我的/text /TabBarItem /TabBar容器能力TabBar提供完整的容器控制能力Prop说明默认值fixed是否固定在底部trueborder是否显示顶部边框trueplaceholderfixed 时是否生成等高占位falsesafeAreaInsetBottom是否开启底部安全区适配truezIndex元素 z-index9992. SCSS 主题定制组件样式全面迁移到 SCSS支持双层级覆盖运行时覆盖推荐在父元素或:root上设置 CSS 自定义属性无需构建配置:root{--mx-tabbar-height:60px;--mx-tabbar-badge-color:#ff6900;--mx-tabbar-item-icon-size:28px;}编译时覆盖通过 vite 的css.preprocessorOptions.scss.additionalData前置定义 SCSS 变量// vite.config.tsexportdefaultdefineConfig({css:{preprocessorOptions:{scss:{additionalData:$mx-tabbar-height: 60px; $mx-tabbar-badge-color: #ff6900;}}}})完整变量列表TabBarCSS 自定义属性SCSS 变量默认值--mx-tabbar-height$mx-tabbar-height50px--mx-tabbar-background$mx-tabbar-background#ffffff--mx-tabbar-border-color$mx-tabbar-border-color#e5e5e5TabBarItemCSS 自定义属性SCSS 变量默认值--mx-tabbar-item-icon-size$mx-tabbar-item-icon-size24px--mx-tabbar-item-font-size$mx-tabbar-item-font-size10px--mx-tabbar-item-gap$mx-tabbar-item-gap2px--mx-tabbar-badge-color$mx-tabbar-badge-color#ee0a24--mx-tabbar-badge-dot-size$mx-tabbar-badge-dot-size8px--mx-tabbar-badge-font-size$mx-tabbar-badge-font-size10px--mx-tabbar-badge-min-width$mx-tabbar-badge-min-width16px--mx-tabbar-badge-line-height$mx-tabbar-badge-line-height16px--mx-tabbar-badge-padding$mx-tabbar-badge-padding0 3px3. TabBarItemProps 类型导出TabBarItemProps从主入口导出用于change事件回调的类型标注importtype{TabBarItemProps}frommeng-xi/uni-routerconstonChange(item:TabBarItemProps,index:number){console.log(item.text,item.to)}三、架构设计组件目录 easycom 规范化components/ ├── router-link/ │ ├── router-link.vue # 组件主体 │ └── type.ts # RouterLinkProps / RouterLinkEmits ├── tab-bar/ │ ├── tab-bar.vue # 父组件provide 上下文 │ ├── context.ts # TabBarContext / TabBarItemProps / TABBAR_KEY │ └── type.ts # TabBarProps / TabBarEmits └── tab-bar-item/ └── tab-bar-item.vue # 子组件inject 上下文父子通信机制TabBar (provide) ├── selectedColor / color (ComputedRef) ├── activePath / activeName (从 useRoute 派生) ├── beforeChange (ComputedRef) ├── register(uid) / unregister(uid) — 子项注册 ├── indexOf(uid) — 响应式索引查询 ├── notifyChange(item, index) — 触发 change 事件 └── notifyError(error) — 触发 error 事件 TabBarItem (inject) ├── ctx.activePath / ctx.activeName → 计算 isActive ├── ctx.register(uid) → onMounted 注册 ├── ctx.unregister(uid) → onUnmounted 注销 ├── ctx.indexOf(uid) → 计算 index └── onClick → beforeChange 拦截 → router.push/replace → notifyChange/notifyError活跃状态判定子组件通过inject(TABBAR_KEY)获取父组件上下文匹配当前路由constisActivecomputed((){if(typeofprops.tostring)returnctx.activePath.valueprops.toif(props.to.name)returnctx.activeName.valueprops.to.nameif(props.to.path)returnctx.activePath.valueprops.to.pathreturnfalse})新增导出// src/index.ts 新增exporttype{TabBarItemProps}from/components/tab-bar/context四、完整使用示例场景带徽标和拦截的底部导航template TabBar selected-color#007aff :bg-color#ffffff :bordertrue :placeholdertrue :safe-area-inset-bottomtrue :before-changebeforeTabChange changeonTabChange erroronTabError TabBarItem to/pages/index/index icon-path/static/tab/home.png selected-icon-path/static/tab/home-active.png text首页 / TabBarItem to/pages/msg/index icon-path/static/tab/msg.png selected-icon-path/static/tab/msg-active.png text消息 :badgeunreadCount / TabBarItem to/pages/vip/index icon-path/static/tab/vip.png selected-icon-path/static/tab/vip-active.png text会员 / TabBarItem to/pages/user/index icon-path/static/tab/user.png selected-icon-path/static/tab/user-active.png text我的 dot / /TabBar /template script setup import type { TabBarItemProps, NavigationFailure } from meng-xi/uni-router const unreadCount ref(3) const beforeTabChange async (item: TabBarItemProps, index: number) { if (item.to.toString().includes(/vip) !isVip()) { uni.showModal({ title: 提示, content: 该功能需要开通会员是否前往开通, success: (res) { if (res.confirm) router.push(/pages/vip/index) } }) return false } return true } const onTabChange (item: TabBarItemProps, index: number) { console.log(切换到: ${item.text} (索引: ${index})) } const onTabError (error: NavigationFailure) { console.error(TabBar 导航失败:, error.message) } /script场景内嵌展示非固定!-- fixedfalse 时 TabBar 作为普通内联元素展示不固定在底部 -- TabBar :fixedfalse :bordertrue selected-color#007aff TabBarItem to/pages/index/index text首页 / TabBarItem to/pages/about/index text关于 :badge5 / /TabBar场景SCSS 主题定制/* 全局样式 - 运行时覆盖 */:root{--mx-tabbar-height:56px;--mx-tabbar-background:#1a1a1a;--mx-tabbar-border-color:#333;--mx-tabbar-badge-color:#ff6900;--mx-tabbar-item-icon-size:26px;--mx-tabbar-item-font-size:11px;}// vite.config.ts - 编译时覆盖exportdefaultdefineConfig({css:{preprocessorOptions:{scss:{additionalData:$mx-tabbar-height: 56px; $mx-tabbar-background: #1a1a1a; $mx-tabbar-badge-color: #ff6900;}}}})升级指南v1.11.0 新增功能向后兼容默认行为不变。组件目录结构变更为 easycom 规范化npm 用户需更新导入路径。迁移说明npm 用户需更新组件导入路径旧路径新路径meng-xi/uni-router/components/RouterLink.vuemeng-xi/uni-router/components/router-link/router-link.vuemeng-xi/uni-router/components/TabBar.vuemeng-xi/uni-router/components/tab-bar/tab-bar.vuemeng-xi/uni-router/components/TabBarItem.vuemeng-xi/uni-router/components/tab-bar-item/tab-bar-item.vueuni_modules 用户无需修改easycom 自动注册RouterLink/TabBar/TabBarItem。easycom 自动注册配置npm 用户可在pages.json中配置 easycom 自定义规则实现自动注册{easycom:{custom:{^router-link$:meng-xi/uni-router/components/router-link/router-link.vue,^tab-bar$:meng-xi/uni-router/components/tab-bar/tab-bar.vue,^tab-bar-item$:meng-xi/uni-router/components/tab-bar-item/tab-bar-item.vue}}}新增导出TabBarItemProps— TabBarchange事件回调参数类型新增组件TabBar— 自定义底部导航栏容器TabBarItem— 导航栏子项需作为 TabBar 子组件使用兼容性package.json的exports通配符./components/*: ./components/*支持嵌套路径新导入路径可正常解析TabBar/TabBarItem为全新组件不影响现有代码SCSS 变量使用!default未覆盖时使用默认值