拓冰建站拓冰建站
首页 / 资讯中心 / 正文

PrimeVue TieredMenu 组件完全指南:嵌套覆盖层菜单的实现原理与实战用法

PrimeVue TieredMenu 组件完全指南嵌套覆盖层菜单的实现原理与实战用法【免费下载链接】primevueNext Generation Vue UI Component Library项目地址: https://gitcode.com/GitHub_Trending/pr/primevueTieredMenu分级菜单是 PrimeVue 中用于展示多级嵌套菜单的组件它将子菜单以嵌套覆盖层nested overlays的形式展开适合承载层级较深的导航结构。本文将基于 PrimeVue 仓库中的官方组件文档与源码实现系统讲解 TieredMenu 的导入方式、基础用法、弹出模式、命令回调、路由导航、模板定制与无障碍支持并深入剖析其底层实现原理帮助你掌握在 Vue 3 项目中快速构建可访问、可定制、支持多级嵌套的菜单。组件概览与导入TieredMenu 的核心能力是以嵌套覆盖层展示子菜单。在 PrimeVue 中各组件均支持按需引入TieredMenu 的导入方式如下import TieredMenu from primevue/tieredmenu;对应源码位于 packages/primevue/src/tieredmenu组件主体由三个文件协作完成TieredMenu.vue主组件负责状态管理、键盘交互、弹出层定位与生命周期TieredMenuSub.vue递归渲染的子菜单组件承担每个层级的列表项渲染与事件上抛BaseTieredMenu.vue声明全部 props 的基础组件。在 showcase 文档站中该组件的演示页面由 apps/showcase/pages/tieredmenu/index.vue 聚合了 Import、Basic、Popup、Template、Command、Router、Accessibility 七个演示章节与本文结构一一对应。Props 与 MenuItem 模型TieredMenu 通过model属性接收菜单项数组每个菜单项遵循 PrimeVue 统一的MenuItem接口定义见 packages/primevue/src/menuitem/MenuItem.d.ts常用字段如下字段类型说明labelstring \| ((...args) string)菜单项显示的文本iconstring菜单项图标样式类如pi pi-fileitemsMenuItem[]子菜单数组存在时该项即为可展开的分级节点command(event: MenuItemCommandEvent) void点击或回车激活菜单项时执行的回调urlstring点击时导航到的外部链接targetstring链接打开方式如_blankdisabledboolean \| ((...args) boolean)是否禁用默认falsevisibleboolean \| ((...args) boolean)是否可见默认trueseparatorboolean设置为true时该项渲染为分隔线style/classany菜单项内联样式 / 样式类keystring菜单项唯一标识除上述字段外MenuItem允许携带任意自定义扩展字段[key: string]: any这为后文模板定制中的badge、shortcut等自定义数据提供了扩展空间。组件的完整 props 在 BaseTieredMenu.vue 中声明类型定义见 TieredMenu.d.tsProp类型默认值说明modelMenuItem[]null菜单项数组popupbooleanfalse是否以弹出覆盖层形式显示appendTostring \| HTMLElementbody弹出层挂载目标可传选择器或 DOM 元素breakpointstring960px移动端适配的断点宽度autoZIndexbooleantrue是否自动管理层级z-indexbaseZIndexnumber0自动分层时使用的基准 z-indexdisabledbooleanfalse是否禁用整个组件tabindexnumber0在 Tab 键顺序中的索引ariaLabelstringnull菜单的无障碍标签ariaLabelledbystringnull引用标签元素的 id基础用法静态多级菜单TieredMenu 直接渲染为一个垂直的多级菜单只需要提供一个model数组TieredMenu :modelitems /菜单数据中通过items字段形成嵌套层级包含items的节点会渲染出指向右侧的子菜单图标并支持展开下一级覆盖层。从源码看主组件在createProcessedItemsTieredMenu.vue中对model进行递归预处理为每个菜单项生成包含index、level、key、parent、parentKey的已处理项processed item这一层抽象是整个键盘导航与子菜单定位的基础。Command 回调command属性定义了菜单项被点击或通过键盘激活时执行的回调。在 TieredMenuSub.vue 中可以看到回调的真实调用链点击菜单项时组件会先以{ originalEvent, item }为参数调用该项的command再向上抛出item-click事件交由主组件处理选中与关闭逻辑。实际使用中常与 Toast 组件配合反馈操作结果完整的 Composition API 示例template div classcard flex justify-center TieredMenu :modelitems / Toast / /div /template script setup import { ref } from vue; import { useToast } from primevue/usetoast; const toast useToast(); const items ref([ { label: File, icon: pi pi-file, items: [ { label: New, icon: pi pi-plus, command: () { toast.add({ severity: success, summary: Success, detail: File created, life: 3000 }); } }, { label: Print, icon: pi pi-print, command: () { toast.add({ severity: error, summary: Error, detail: No printer connected, life: 3000 }); } } ] }, { label: Search, icon: pi pi-search, command: () { toast.add({ severity: warn, summary: Search Results, detail: No results found, life: 3000 }); } }, { separator: true }, { label: Sync, icon: pi pi-cloud, items: [ { label: Import, icon: pi pi-cloud-download, command: () { toast.add({ severity: info, summary: Downloads, detail: Downloaded from cloud, life: 3000 }); } }, { label: Export, icon: pi pi-cloud-upload, command: () { toast.add({ severity: info, summary: Shared, detail: Exported to cloud, life: 3000 }); } } ] } ]); /script示例中同时展示了separator: true分隔线的用法。注意 Toast 使用前需在应用内引入ToastService详见 apps/showcase/app.vue 的插件注册方式。Popup 弹出模式当需要将菜单作为弹出覆盖层如右键菜单、按钮下拉菜单使用时添加popup属性并通过菜单ref调用toggle方法传入目标元素的触发事件Button typebutton labelToggle clicktoggle aria-haspopuptrue aria-controlsoverlay_tmenu / TieredMenu refmenu idoverlay_tmenu :modelitems popup /template div classcard flex justify-center Button typebutton labelToggle clicktoggle aria-haspopuptrue aria-controlsoverlay_tmenu / TieredMenu refmenu idoverlay_tmenu :modelitems popup / /div /template script setup import { ref } from vue; const menu ref(); const items ref([ { label: File, icon: pi pi-file, items: [ { label: New, icon: pi pi-plus, items: [ { label: Document, icon: pi pi-file }, { label: Image, icon: pi pi-image }, { label: Video, icon: pi pi-video } ] }, { label: Open, icon: pi pi-folder-open }, { label: Print, icon: pi pi-print } ] }, { label: Edit, icon: pi pi-file-edit, items: [ { label: Copy, icon: pi pi-copy }, { label: Delete, icon: pi pi-times } ] }, { label: Search, icon: pi pi-search }, { separator: true }, { label: Share, icon: pi pi-share-alt, items: [ { label: Slack, icon: pi pi-slack }, { label: Whatsapp, icon: pi pi-whatsapp } ] } ]); const toggle (event) { menu.value.toggle(event); }; /script在弹出模式下组件暴露了三个实例方法见 TieredMenu.d.tstoggle(event)根据当前可见状态在显示与隐藏之间切换show(event)显示弹出层hide()隐藏弹出层。从源码 TieredMenu.vue 可以看到toggle内部根据visible状态分发到show/hideshow会记录event.currentTarget作为定位锚点并发出before-show事件hide则清空activeItemPath与焦点信息并发出before-hide。弹出层通过Portal组件挂载到appendTo指定的目标默认body并通过transition与onEnter中的absolutePosition完成覆盖层定位与对齐TieredMenu.vue。弹出层还内置了一系列自动关闭策略点击菜单外部时通过outsideClickListener隐藏、滚动目标容器时通过ConnectedOverlayScrollHandler隐藏、窗口 resize 时在非触屏设备上隐藏均可在源码bindOutsideClickListener、bindScrollListener、bindResizeListener中逐一印证。Router 导航集成带导航功能的菜单项通过#item模板定制从而支持router-link组件、外部链接与编程式导航三种方式TieredMenu :modelitems template #item{ item, props, hasSubmenu } router-link v-ifitem.route v-slot{ href, navigate } :toitem.route custom a v-ripple :hrefhref v-bindprops.action clicknavigate span :classitem.icon / span classml-2{{ item.label }}/span /a /router-link a v-else v-ripple :hrefitem.url :targetitem.target v-bindprops.action span :classitem.icon / span classml-2{{ item.label }}/span span v-ifhasSubmenu classpi pi-angle-right ml-auto / /a /template /TieredMenu完整示例item.route走 vue-router、item.url走外部链接、command走编程式导航template div classcard flex justify-center TieredMenu :modelitems template #item{ item, props, hasSubmenu } router-link v-ifitem.route v-slot{ href, navigate } :toitem.route custom a v-ripple :hrefhref v-bindprops.action clicknavigate span :classitem.icon / span classml-2{{ item.label }}/span /a /router-link a v-else v-ripple :hrefitem.url :targetitem.target v-bindprops.action span :classitem.icon / span classml-2{{ item.label }}/span span v-ifhasSubmenu classpi pi-angle-right ml-auto / /a /template /TieredMenu /div /template script setup import { ref } from vue; import { useRouter } from vue-router; const router useRouter(); const items ref([ { label: Router, icon: pi pi-palette, items: [ { label: Styled, route: /theming/styled }, { label: Unstyled, route: /theming/unstyled } ] }, { label: Programmatic, icon: pi pi-link, command: () { router.push(/introduction); } }, { label: External, icon: pi pi-home, items: [ { label: Vue.js, url: https://vuejs.org/ }, { label: Vite.js, url: https://vuejs.org/ } ] } ]); /script这里用到的#item模板插槽作用域包含三个参数item当前菜单项实例、props由 TieredMenuSub.vue 的getMenuItemProps生成的action/icon/label/submenuicon绑定对象、hasSubmenu当前项是否包含子菜单。当使用自定义#item模板时默认的a链接渲染被完全接管因此必须自行通过v-bindprops.action应用无障碍与样式相关的绑定。模板定制图标、角标与快捷键#item模板还允许在菜单项中自由混入 Badge、快捷键提示等自定义内容例如为子菜单项添加shortcut字段显示键盘快捷键、为项添加badge字段显示计数角标TieredMenu :modelitems template #item{ item, props, hasSubmenu } a v-ripple classflex items-center v-bindprops.action span :classitem.icon / span classml-2{{ item.label }}/span Badge v-ifitem.badge classml-auto :valueitem.badge / span v-ifitem.shortcut classml-auto border border-surface rounded bg-emphasis text-muted-color text-xs p-1{{ item.shortcut }}/span i v-ifhasSubmenu classpi pi-angle-right ml-auto/i /a /template /TieredMenu由于MenuItem支持任意扩展字段badge、shortcut这类自定义数据无需额外声明即可直接读取。数据模型示例script setup import { ref } from vue; const items ref([ { label: File, icon: pi pi-file, items: [ { label: New, icon: pi pi-plus, items: [ { label: Document, icon: pi pi-file, shortcut: ⌘N }, { label: Image, icon: pi pi-image, shortcut: ⌘I }, { label: Video, icon: pi pi-video, shortcut: ⌘L } ] }, { label: Open, icon: pi pi-folder-open, shortcut: ⌘O }, { label: Print, icon: pi pi-print, shortcut: ⌘P } ] }, { label: Edit, icon: pi pi-file-edit, items: [ { label: Copy, icon: pi pi-copy, shortcut: ⌘C }, { label: Delete, icon: pi pi-times, shortcut: ⌘D } ] }, { label: Search, icon: pi pi-search, shortcut: ⌘S }, { separator: true }, { label: Share, icon: pi pi-share-alt, items: [ { label: Slack, icon: pi pi-slack, badge: 2 }, { label: Whatsapp, icon: pi pi-whatsapp, badge: 3 } ] } ]); /script除#item外组件还提供#start、#end插槽分别渲染在根列表之前与之后见 TieredMenu.vue、#itemicon与#submenuicon插槽用于定制图标完整插槽签名可在 TieredMenu.d.ts 中查阅。事件与生命周期组件对外发出的事件定义于 TieredMenu.d.ts事件触发时机focus组件获得焦点时blur组件失去焦点时before-show弹出层显示之前before-hide弹出层隐藏之前show弹出层显示完成后动画进入结束hide弹出层隐藏时动画离开开始无障碍与键盘支持TieredMenu 对屏幕阅读器有着完整的语义支持。菜单根元素使用menubar角色并设置aria-orientationvertical菜单的可访问名称通过aria-labelledby或aria-label提供每个列表项使用menuitem角色aria-label指向该项文本禁用项会设置aria-disabled。子菜单使用menu角色并通过aria-labelledby关联到其根菜单项的 label id可展开子菜单的项会带有aria-haspopup与aria-expanded。弹出模式下组件还会隐式管理目标元素的aria-expanded、aria-haspopup与aria-controls这正是官方示例中按钮需要手动标注aria-haspopup与aria-controls的原因TieredMenuSub.vue。键盘支持由主组件的onKeyDown分发处理TieredMenu.vue完整按键行为如下按键行为tab焦点进入菜单时聚焦第一项焦点已在菜单内时移动到页面 Tab 序列中的下一个可聚焦元素shift tab焦点进入菜单时聚焦第一项焦点已在菜单内时移动到上一个可聚焦元素enter若菜单项含子菜单则展开子菜单否则激活该项并关闭所有已打开的覆盖层space与enter行为一致escape焦点位于弹出式子菜单内时关闭该子菜单并将焦点移回被关闭子菜单的根项down arrow在当前子菜单内向下移动焦点up arrow在当前子菜单内向上移动焦点alt up arrow关闭弹出层并将焦点移回目标元素right arrow若选项已关闭则展开它否则将焦点移到第一个子选项left arrow若选项已展开则关闭它否则将焦点移到父选项home将焦点移到当前子菜单的第一项end将焦点移到当前子菜单的最后一项任意可打印字符将焦点移动到标签以输入字符开头的菜单项支持连续输入500ms 无输入后重置搜索词见源码searchItems其中字符快速定位由searchItems方法实现它会累积用户输入的字符序列在可见项中寻找label前缀匹配的项并移动焦点配合scrollInView保证被聚焦项始终滚动到可视区域内TieredMenu.vue。样式定制与主题化TieredMenu 的全部样式类在 style/TieredMenuStyle.js 中定义包括p-tieredmenu、p-tieredmenu-root-list、p-tieredmenu-item、p-tieredmenu-item-link、p-tieredmenu-submenu、p-tieredmenu-separator等弹出模式下根元素额外带有p-tieredmenu-overlay类当视口宽度低于breakpoint默认960px时根元素还会追加p-tieredmenu-mobile类源码中通过matchMedia监听实现TieredMenu.vue。组件支持 PrimeVue 完整的主题化体系可通过pt/ptOptions进行 passthrough 定制root、rootList、item、itemContent、itemLink、itemIcon、itemLabel、submenuIcon、separator、submenu、transition等属性目标见 TieredMenu.d.ts通过dt传入设计令牌以及通过unstyled移除内置样式后配合 Tailwind 等方案完全自绘。底层 CSS 变量与样式令牌由primeuix/styles/tieredmenu提供主题相关演示可参考 showcase 中的 doc/tieredmenu/theming 目录。小结TieredMenu 以递归的子菜单组件TieredMenuSub与预处理后的菜单模型createProcessedItems为骨架配合弹出层定位、事件总线、ZIndex 管理、键盘导航与字符快速搜索构成了一个功能完备的多级嵌套菜单方案。实际开发中按需引入组件、用MenuItem数组描述层级、根据场景在静态渲染与popup弹出模式间切换再借助#item模板接入路由或自定义 UI即可覆盖绝大多数菜单交互需求。【免费下载链接】primevueNext Generation Vue UI Component Library项目地址: https://gitcode.com/GitHub_Trending/pr/primevue创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门