news 2026/9/27 10:05:44

ng-zorro-antd Menu 导航菜单组件完全指南:从 API 到源码实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ng-zorro-antd Menu 导航菜单组件完全指南:从 API 到源码实现
  • UI组件
  • 前端

【免费下载链接】ng-zorro-antd

Angular UI Component Library based on Ant Design

项目地址:https://gitcode.com/gh_mirrors/ng/ng-zorro-antd
点击查看免费下载

本指南围绕 ng-zorro-antd(Angular UI 组件库)中的 Menu 导航菜单组件展开,完整覆盖nz-menu、nz-menu-item、nz-submenu、nz-menu-group、nz-menu-divider五个核心指令的 API 与实战用法,并结合组件源码深入剖析模式切换、路由联动、内联折叠、弹出层定位等底层实现原理。读完本文,你将能够在 Angular 项目中独立搭建顶栏、侧边栏及多级嵌套导航菜单,并理解其状态管理机制,为二次定制打好基础。

何时使用

导航菜单对网站而言至关重要,它帮助用户快速从一个站点分区跳转到另一个分区。常见的导航形态包括:

  • 顶部导航(Top Navigation):提供网站的所有分类与功能入口,通常采用horizontal(水平)模式;
  • 侧边导航(Side Navigation):提供网站的多层级结构,通常采用inline(内联)或vertical(垂直)模式。

更多与导航相关的布局方案可参考 Layout 布局组件。

快速上手:最小可用示例

NzMenuModule导出全部菜单相关指令,在模块或组件中导入即可使用(参见 menu.module.ts):

<ul nz-menu> <li nz-menu-item>Menu 1</li> <li nz-menu-item>Menu 2</li> <li nz-submenu nzTitle="SubMenu Title"> <ul> <li nz-menu-item>SubMenu Item 1</li> <li nz-menu-item>SubMenu Item 2</li> <li nz-menu-item>SubMenu Item 3</li> </ul> </li> </ul>

这是最基础的组合方式:[nz-menu]作用于ul根节点,[nz-menu-item]表示叶子菜单项,[nz-submenu]包裹子菜单并以内部ul承载下级菜单项。默认情况下,菜单以vertical(垂直)模式、light(浅色)主题渲染。

[nz-menu]根指令

[nz-menu]通过NzMenuDirective实现(menu.directive.ts),它负责菜单的模式、主题、折叠状态等全局配置。

参数说明类型默认值
[nzInlineCollapsed]菜单inline模式时是否折叠boolean-
[nzInlineIndent]inline模式下每级菜单项的缩进像素值number24
[nzMode]菜单类型,支持vertical、horizontal、inline三种模式'vertical' \| 'horizontal' \| 'inline''vertical'
[nzSelectable]是否允许选中菜单项booleantrue
[nzTheme]菜单配色主题'light' \| 'dark''light'
(nzClick)点击nz-menu内nz-menu-item时的输出事件EventEmitter<NzMenuItemComponent>

模式(nzMode)与主题(nzTheme)

三种模式与两种主题的类型定义位于 menu.types.ts:

export type NzMenuModeType = 'vertical' | 'horizontal' | 'inline'; export type NzMenuThemeType = 'light' | 'dark';

从源码看,nzMode与nzInlineCollapsed会通过combineLatest组合成一个派生状态actualMode:当inline模式且折叠(nzInlineCollapsed = true)时,实际渲染模式会被推导为vertical,随后通过MenuService.setMode()广播给所有子组件(menu.directive.ts):

this.actualMode = inlineCollapsed ? 'vertical' : mode; this.nzMenuService.setMode(this.actualMode);

因此“折叠后的 inline 菜单”本质上是垂直模式 + 窄宽度布局,这一设计避免了折叠时子菜单内联展开的尴尬,同时保持弹出式子菜单可用。

点击事件(nzClick)与选中逻辑

nzClick输出源自MenuService.descendantMenuItemClick$流。默认情况下(nzSelectable = true),点击某个菜单项后,菜单会遍历所有nz-menu-item并把选中态收敛到被点击的那一项(menu.directive.ts):

this.nzMenuService.descendantMenuItemClick$.subscribe(menu => { this.nzClick.emit(menu); if (this.nzSelectable && !menu.nzMatchRouter) { this.listOfNzMenuItemDirective.forEach(item => item.setSelectedState(item === menu)); } });

值得注意的是:当菜单项启用了路由匹配(nzMatchRouter)时,选中态改由路由驱动,手动点击不再接管,二者不会互相打架。

缩进(nzInlineIndent)

nzInlineIndent默认值为24(menu.directive.ts)。它会通过MenuService.setInlineIndent()下发,nz-menu-item与nz-submenu在inline模式下按层级计算level * inlineIndent的padding-inline-start,实现逐级缩进(见 menu-item.component.ts)。

[nz-menu-item]菜单项

[nz-menu-item]由NzMenuItemComponent实现(menu-item.component.ts),渲染为li.ant-menu-item,内部通过<ng-content>投影用户内容。

参数说明类型默认值
[nzDisabled]是否禁用菜单项booleanfalse
[nzSelected]菜单项是否选中booleanfalse
[nzMatchRouter]是否根据routerLink自动设置nzSelectedbooleanfalse
[nzMatchRouterExact]仅当 URL 与链接完全匹配时才选中,语义同routerLinkActiveOptionsbooleanfalse
[nzDanger]显示危险样式(红色)booleanfalse

禁用项的行为

源码中,禁用菜单项在点击时会调用preventDefault()与stopPropagation(),直接阻断事件冒泡与默认行为(menu-item.component.ts):

clickMenuItem(e: MouseEvent): void { if (this.nzDisabled) { e.preventDefault(); e.stopPropagation(); return; } ... }

与 Router 的路由联动

nzMatchRouter的实现依赖注入的Router与RouterLink:组件在NavigationEnd事件后检查链接是否激活,并通过router.isActive计算激活状态(menu-item.component.ts):

router.isActive(link.urlTree || '', { paths: this.nzMatchRouterExact ? 'exact' : 'subset', queryParams: this.nzMatchRouterExact ? 'exact' : 'subset', fragment: 'ignored', matrixParams: 'ignored' });

即:nzMatchRouterExact = false时采用subset匹配(URL 子集匹配,如/home/users可激活/home);true时采用exact精确匹配。完整可运行示例见 demo/router.ts。

[nz-submenu]子菜单

子菜单是菜单组件中最复杂的部分,同时支持内联展开与弹出层两种渲染形态。

三种设置标题的方式

<!-- 方式一:属性字符串 --> <li nz-submenu nzTitle="SubTitle" nzIcon="appstore"></li> <!-- 方式二:[title] 内容投影 --> <li nz-submenu> <span title> <nz-icon nzType="appstore" /> <span>SubTitle</span> </span> </li> <!-- 方式三:TemplateRef 模板 --> <li nz-submenu [nzTitle]="titleTpl"></li> <ng-template #titleTpl> <nz-icon nzType="appstore" /> <span>SubTitle</span> </ng-template>

三种方式分别对应字符串属性、[title]选择器投影与TemplateRef<void>,灵活性覆盖从纯文本到富模板的所有场景。若使用方式一,nzIcon可直接指定标题前的图标类型。

参数表

参数说明类型默认值
[nzPlacement]弹出菜单的位置'bottomLeft' \| 'bottomCenter' \| 'bottomRight' \| 'topLeft' \| 'topCenter' \| 'topRight''bottomLeft'
[nzOpen]子菜单是否展开,支持双向绑定booleanfalse
[nzDisabled]是否禁用子菜单booleanfalse
[nzTitle]子菜单标题string \| TemplateRef<void>-
[nzIcon]标题中的图标类型string-
[nzMenuClassName]自定义子菜单容器的 class 名称string-
[nzTriggerSubMenuAction]触发子菜单展开/收起的行为'hover' \| 'click''hover'
(nzOpenChange)nzOpen变化回调EventEmitter<boolean>-

内联模式与弹出模式的分流

从 submenu.component.ts 的模板可以看到,nz-submenu依据当前mode分流渲染:

  • inline模式:使用nz-submenu-inline-child以内联方式渲染下级ul,配合展开动画;
  • 其他模式(vertical / horizontal):基于 Angular CDK 的cdkConnectedOverlay渲染弹出层,弹出宽度在 horizontal 模式下会同步触发源宽度(setTriggerWidth()),保证弹出菜单与标题等宽。

垂直模式下弹出层的位置候选集固定为右侧(rightTop / right / rightBottom)与左侧(leftTop / left / leftBottom)六种(submenu.component.ts),水平模式则使用底部/顶部的水平位置候选集;nzPlacement用于指定首选位置,CDK Overlay 会在空间不足时自动回退到候选位置。

展开状态的管理:150ms 防抖

nzOpen的展开状态由NzSubmenuService(submenu.service.ts)统一管理,其核心逻辑是:

  • 子菜单点击某个菜单项后自动收起(非 inline 模式或位于 Dropdown 中时);
  • 标题与弹出层区域的mouseenter / mouseleave状态与子级子菜单展开状态通过combineLatest合并,再经auditTime(150)防抖,避免鼠标在标题与弹出层之间快速移动时子菜单闪烁抖动;
  • 打开状态会向上传播给父级NzSubmenuService,因此嵌套多级子菜单可以整链展开。

另外,NzSubmenuService会对模式做一层推导:inline保持不变,垂直模式或处于其他子菜单内部的子菜单统一按vertical处理,水平模式仅作用于顶级子菜单(submenu.service.ts)。

嵌套层级与缩进

NzSubmenuService通过skipSelf注入宿主NzSubmenuService来计算层级level(嵌套子菜单level + 1),inline模式下padding-inline-start按level * inlineIndent递进,多级结构天然缩进正确。

[nz-menu-group]菜单分组

[nz-menu-group]用于给一组菜单项加分组标题,由NzMenuGroupComponent实现(menu-group.component.ts)。

同样支持三种标题设置方式:

<li nz-menu-group nzTitle="SubTitle" nzIcon="appstore"></li> <li nz-menu-group> <span title> <nz-icon nzType="appstore" /> <span>SubTitle</span> </span> </li> <li nz-menu-group [nzTitle]="titleTpl"></li> <ng-template #titleTpl> <nz-icon nzType="appstore" /> <span>SubTitle</span> </ng-template>
参数说明类型默认值
[nzTitle]设置菜单分组标题string \| TemplateRef<void>-

nzTitle通过nzStringTemplateOutlet统一支持字符串与模板两种输入(menu-group.component.ts),内部还会自动为紧随标题的ul添加ant-menu-item-group-list类。

分组与嵌套子菜单的组合示例可参考 demo/theme.ts:在nz-submenu内嵌套nz-menu-group,每组再包含若干nz-menu-item。

[nz-menu-divider]菜单分割线

[nz-menu-divider]是一条作用于菜单项之间的分割线。需要特别注意的是,官方文档明确说明:它仅用于垂直弹出菜单或 Dropdown 菜单中(即vertical弹出层与nz-dropdown内部),不用于内联菜单。

其实现极简,仅为宿主元素附加ant-dropdown-menu-item-divider类(menu-divider.directive.ts),这也印证了它与 Dropdown 菜单样式的绑定关系:

<li nz-menu-divider></li>

主题切换:light 与 dark

菜单主题通过nzTheme在'light'与'dark'之间切换,对应 style/light.less 与 style/dark.less 两套样式入口。

在实际应用中,主题常与状态控件联动。仓库示例 demo/theme.ts 展示了用nz-switch动态切换明暗主题的写法:

<nz-switch [(ngModel)]="theme"> <span checked>Dark</span> <span unchecked>Light</span> </nz-switch> <ul nz-menu nzMode="inline" [nzTheme]="theme ? 'dark' : 'light'"> ... </ul>

主题变化通过MenuService.setTheme()广播(menu.service.ts),nz-submenu会订阅theme$同步更新弹出层的主题(submenu.component.ts),保证弹出子菜单与主菜单明暗一致。

折叠模式:inline-collapsed

当菜单为inline模式并希望收缩成仅图标的窄条(常用于 Sider 折叠)时,使用nzInlineCollapsed。仓库示例 demo/inline-collapsed.ts 展示了与nz-sider及开关按钮配合的典型场景。

折叠背后的实现逻辑值得注意(menu.directive.ts):

  • 折叠前先记录当前所有已展开的子菜单(nzOpen为真的子菜单),再全部收起;
  • 取消折叠时,按记录恢复这些子菜单的展开状态。

这保证了折叠-展开往返过程中,用户此前打开的多级子菜单状态能够被完整还原。

与 Layout 结合:侧边导航

菜单组件与 Layout 布局组件 的nz-sider是黄金搭档。仓库 demo/sider-current.ts 展示了“Sider 折叠 → 菜单同步折叠 → 路由联动高亮”的完整侧边导航方案,配合nz-menu-item的nzMatchRouter可实现刷新后选中态与当前路由保持一致。

模式切换实战

nzMode支持在vertical、horizontal、inline三种模式间动态切换,仓库示例 demo/switch-mode.ts 与 demo/horizontal.ts 分别演示了运行时切换模式与顶部水平导航。源码层面,nzMode变化时会先把所有子菜单强制收起(menu.directive.ts),避免旧模式遗留的展开状态污染新模式布局。

递归菜单与动态数据

当菜单层级不确定时,可采用组件递归渲染。仓库 demo/recursive.ts 给出了一份自递归组件的参考实现:以nz-menu-item为叶子、nz-submenu为分支,通过<ng-template>或递归组件消费菜单树数据,适用于后端动态下发导航结构。

源码架构速览

理解菜单的底层结构有助于二次开发与问题排查:

  • menu.directive.ts:根指令,聚合模式/主题/折叠/缩进等全局状态,负责选中态收敛与nzClick广播;
  • menu.service.ts:菜单全局状态中心,持有theme$、mode$、inlineIndent$、isChildSubMenuOpen$等 BehaviorSubject,以及点击事件流descendantMenuItemClick$/childMenuItemClick$;
  • submenu.service.ts:每个nz-submenu的局部状态服务,管理自身展开状态、层级与模式推导,并向上传播打开状态;
  • menu.token.ts:两个内部 DI Token,NzIsMenuInsideDropdownToken标记菜单是否位于 Dropdown 内,NzMenuServiceLocalToken供嵌套菜单获取本地服务实例;
  • menu-item.component.ts:菜单项,负责禁用、选中、危险样式与路由联动;
  • submenu.component.ts:子菜单,按模式分流内联展开与 CDK Overlay 弹出层。

组件树通过依赖注入天然形成层级:nz-submenu提供局部NzSubmenuService,内部的nz-menu-item与嵌套nz-submenu通过skipSelf向上获取父级服务,从而完成层级计算与打开状态沿链传播;同时NzMenuDirective在提供者中通过MenuServiceFactory区分“根菜单服务”与“外部 Dropdown 注入的菜单服务”(menu.directive.ts),使nz-menu既能在页面中独立使用,也能嵌入nz-dropdown菜单体系。

总结

ng-zorro-antd 的 Menu 组件围绕「根指令 + 服务层 + 层级注入」的架构,提供了vertical/horizontal/inline三种模式、light/dark双主题、内联折叠、路由联动、弹出子菜单与递归渲染等完整能力。掌握本文的 API 表格与源码要点后,你可以快速构建从简单顶栏到复杂侧边多级导航的各类场景;遇到展开/收起异常或层级缩进问题时,也能顺着MenuService与NzSubmenuService的调用链快速定位根因。

  • UI组件
  • 前端

【免费下载链接】ng-zorro-antd

Angular UI Component Library based on Ant Design

项目地址:https://gitcode.com/gh_mirrors/ng/ng-zorro-antd
点击查看免费下载
上一篇:手机号码归属地定位工具:3分钟实现快速免费地理位置查询
下一篇:XUnity.AutoTranslator完全指南:让Unity游戏自动翻译成中文的终极方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 10:04:46

做网站文字要求图解步骤:3步避坑指南

做网站文字要求图解步骤:3步避坑指南 找建站公司怕被坑高价?别急,先看清 做网站文字要求 的 图解步骤 。很多老板以为文字就是“写点介绍”,其实这是SEO和转化的核心。不懂这里,网站做完就是“死页”,钱白扔。 运营目标与指标:文字不是装饰,是转化引擎…

作者头像 李华
网站建设 2026/9/27 10:04:06

百度怎么对网站处罚实战对比评测:备案避坑全解析

百度怎么对网站处罚实战对比评测:备案避坑全解析 做站最怕啥?不是代码报错,也不是服务器宕机,而是辛辛苦苦运营的站点,突然有一天在百度的搜索结果里彻底消失。你打开浏览器,地址栏显示“该网站未备案”或者“违规封禁”。这时候你才反应过来: 备案流程一头雾水 ,当初为了省事选的低质服务,现在全得自己填坑。…

作者头像 李华
网站建设 2026/9/27 10:03:56

网页编辑与网站编辑避坑速查手册:备案卡壳自救指南

网页编辑与网站编辑避坑速查手册:备案卡壳自救指南 备案流程一头雾水?别急,这份网页编辑与网站编辑速查手册能救急。很多站长卡在域名解析和服务器配置上,其实核心就三步。 网页编辑与网站编辑的区别到底在哪?…

作者头像 李华
网站建设 2026/9/27 10:03:49

2026最新手机网页如何制作:告别模板丑陋,SEO实战指南

2026最新手机网页如何制作:告别模板丑陋,SEO实战指南 还在为模板网站的丑和僵化头疼?很多老板看着网上那些千篇一律的模板,心里直嘀咕:这玩意儿根本没法用,既显不出公司实力,又抓不住用户。 2026年的移动端流量竞争已经卷到了极致, 手机网页如何制作…

作者头像 李华
网站建设 2026/9/27 10:03:31

3个真实案例看学院网站建设项目概述最佳实践避坑

3个真实案例看学院网站建设项目概述最佳实践避坑 域名买错了,服务器选慢了,项目直接延期。这是上周某高校教务处老师跟我吐槽的原话。他们想做学院官网改版,结果卡在基础设施上,前端代码写了一半,后台数据没地方存,SEO权重全白搭。别觉得这是小概率事件,我干了十年建站,光“域名服务器搞不懂”导致的烂尾项目,…

作者头像 李华
网站建设 2026/9/27 10:03:31

PHP大型网站开发视频学习避坑指南与部署注意事项

PHP大型网站开发视频学习避坑指南与部署注意事项 找建站公司怕被坑高价?这几乎是每个独立站长的噩梦。很多小白看到报价单上的数字直接劝退,要么选择廉价模板站导致后期维护…

作者头像 李华