 Dropdown 下拉菜单:完整 API 详解与源码实现剖析)
Element (element-ui) Dropdown 下拉菜单完整 API 详解与源码实现剖析【免费下载链接】elementA Vue.js 2.0 UI Toolkit for Web项目地址: https://gitcode.com/gh_mirrors/eleme/element本文基于 ElementA Vue.js 2.0 UI Toolkit for Web官方文档中的 Dropdown 组件说明展开覆盖el-dropdown/el-dropdown-menu/el-dropdown-item三个组件的完整用法默认插槽触发器、split-button按钮组模式、trigger触发方式、hide-on-click菜单保持、command事件分发机制与size尺寸适配并逐条给出属性、插槽、事件的官方 API 表。读完本文你可以直接在 Vue 2 项目中落地下拉菜单的任意交互形态并能从源码层面理解command事件的组件间通信链路、hover 延迟控制与键盘可访问性实现。基本用法Uso básico默认情况下将鼠标悬停在触发元素上即可展开菜单无需点击。触发元素由默认插槽default slot承载菜单部分由名为dropdown的插槽承载el-dropdown span classel-dropdown-link Dropdown Listi classel-icon-arrow-down el-icon--right/i /span el-dropdown-menu slotdropdown el-dropdown-itemAction 1/el-dropdown-item el-dropdown-itemAction 2/el-dropdown-item el-dropdown-itemAction 3/el-dropdown-item el-dropdown-item disabledAction 4/el-dropdown-item el-dropdown-item dividedAction 5/el-dropdown-item /el-dropdown-menu /el-dropdown style .el-dropdown-link { cursor: pointer; color: #409EFF; } .el-icon-arrow-down { font-size: 12px; } /style从源码 dropdown.vue 的render函数可以看到非split-button模式下触发元素直接取自this.$slots.default并被包裹进一个绑定v-clickoutside{hide}的div classel-dropdown中——这意味着点击组件外部任意位置都会触发hide()这是下拉菜单“点击外部收起”行为的来源。官方 API 表对默认插槽有明确要求它必须是一个合法的 DOM 元素如span、button或el-component组件因为触发事件监听器listener需要直接挂载到这个元素上。源码中 initEvent() 通过this.$slots.default[0].elm拿到该真实 DOM 节点并注册事件若插槽内容是多个并列节点或纯文本事件绑定将失效。触发元素Elemento detonante除了自定义span官方文档还展示了用按钮触发菜单的方式以及split-button属性把触发元素拆分为按钮组左侧是普通按钮执行自身点击逻辑右侧才是真正触发下拉的“箭头按钮”el-dropdown el-button typeprimary Dropdown Listi classel-icon-arrow-down el-icon--right/i /el-button el-dropdown-menu slotdropdown el-dropdown-itemAction 1/el-dropdown-item el-dropdown-itemAction 2/el-dropdown-item el-dropdown-itemAction 3/el-dropdown-item el-dropdown-itemAction 4/el-dropdown-item el-dropdown-itemAction 5/el-dropdown-item /el-dropdown-menu /el-dropdown el-dropdown split-button typeprimary clickhandleClick Dropdown List el-dropdown-menu slotdropdown el-dropdown-itemAction 1/el-dropdown-item el-dropdown-itemAction 2/el-dropdown-item el-dropdown-itemAction 3/el-dropdown-item el-dropdown-itemAction 4/el-dropdown-item el-dropdown-itemAction 5/el-dropdown-item /el-dropdown-menu /el-dropdown style .el-dropdown { vertical-align: top; } .el-dropdown .el-dropdown { margin-left: 15px; } .el-icon-arrow-down { font-size: 12px; } /style script export default { methods: { handleClick() { alert(button click); } } } /script关于split-button的源码实现细节见 dropdown.vue开启split-button后render会生成一个el-button-group内含两个按钮第一个按钮承载默认插槽内容绑定handleMainButtonClick先$emit(click, event)再hide()第二个按钮带有el-dropdown__caret-button类名和reftrigger内部渲染el-dropdown__icon el-icon-arrow-down图标。箭头按钮左侧的竖分隔线并非真实 DOM 元素而是由样式 dropdown.scss 中::before伪元素绘制的 1px 半透明分隔线hover 非禁用状态时会撑满整个高度。此时触发监听器挂载对象从默认插槽切换为this.$refs.trigger.$el箭头按钮本身即 initEvent() 中的三元判断splitButton ? this.$refs.trigger.$el : this.$slots.default[0].elm。type属性如primary仅对两个按钮生效因此官方说明中特别标注“sólo funciona cuandosplit-buttones true”仅在split-button为 true 时生效。单元测试 dropdown.spec.js 的split button用例验证了该行为点击左侧.el-button会触发click事件回调而 hover 的挂载点在.el-dropdown__caret-button上mouseenter 后dropdown.visible变为true。触发方式Cómo detonar el evento通过trigger属性控制展开方式可选值为hover默认与clickel-row classblock-col-2 el-col :span12 span classdemonstrationhover to trigger/span el-dropdown span classel-dropdown-link Dropdown Listi classel-icon-arrow-down el-icon--right/i /span el-dropdown-menu slotdropdown el-dropdown-item iconel-icon-plusAction 1/el-dropdown-item el-dropdown-item iconel-icon-circle-plusAction 2/el-dropdown-item el-dropdown-item iconel-icon-circle-plus-outlineAction 3/el-dropdown-item el-dropdown-item iconel-icon-checkAction 4/el-dropdown-item el-dropdown-item iconel-icon-circle-checkAction 5/el-dropdown-item /el-dropdown-menu /el-dropdown /el-col el-col :span12 span classdemonstrationclick to trigger/span el-dropdown triggerclick span classel-dropdown-link Dropdown Listi classel-icon-arrow-down el-icon--right/i /span el-dropdown-menu slotdropdown el-dropdown-item iconel-icon-plusAction 1/el-dropdown-item el-dropdown-item iconel-icon-circle-plusAction 2/el-dropdown-item el-dropdown-item iconel-icon-circle-plus-outlineAction 3/el-dropdown-item el-dropdown-item iconel-icon-checkAction 4/el-dropdown-item el-dropdown-item iconel-icon-circle-checkAction 5/el-dropdown-item /el-dropdown-menu /el-dropdown /el-col /el-row style .el-dropdown-link { cursor: pointer; color: #409EFF; } .el-icon-arrow-down { font-size: 12px; } .demonstration { display: block; color: #8492a6; font-size: 14px; margin-bottom: 20px; } /style从 initEvent() 的实现看两种触发方式的差异体现在事件绑定上trigger hover时触发元素与菜单 DOM 上都注册了mouseenter → show()和mouseleave → hide()。把监听同时挂到菜单本身是为了保证鼠标从触发器移动到菜单过程中不会因离开触发器而过早收起trigger click时触发元素只注册click → handleClick()在visible状态下切换展开/收起。hover 延迟show-timeout 与 hide-timeout两种延迟参数仅在trigger为hover时生效click模式下延迟恒为 0这一点可以直接从 show() / hide() 看出show() { if (this.disabled) return; clearTimeout(this.timeout); this.timeout setTimeout(() { this.visible true; }, this.trigger click ? 0 : this.showTimeout); }, hide() { if (this.disabled) return; // ...tabindex 重置... clearTimeout(this.timeout); this.timeout setTimeout(() { this.visible false; }, this.trigger click ? 0 : this.hideTimeout); }show()与hide()共享同一个this.timeout因此“快速移入又移出”时后到的调用会先clearTimeout掉前一次的定时器——这是防抖式互斥的实现方式。默认值为show-timeout: 250ms、hide-timeout: 150ms。菜单隐藏控制Ocultamiento del menúhide-on-click属性boolean默认true决定点击菜单项后菜单是否自动关闭。默认情况下点击任一项菜单即收起设为false可保持菜单展开适合“批量勾选多个操作”的场景el-dropdown :hide-on-clickfalse span classel-dropdown-link Dropdown Listi classel-icon-arrow-down el-icon--right/i /span el-dropdown-menu slotdropdown el-dropdown-itemAction 1/el-dropdown-item el-dropdown-itemAction 2/el-dropdown-item el-dropdown-itemAction 3/el-dropdown-item el-dropdown-item disabledAction 4/el-dropdown-item el-dropdown-item dividedAction 5/el-dropdown-item /el-dropdown-menu /el-dropdown style .el-dropdown-link { cursor: pointer; color: #409EFF; } .el-icon-arrow-down { font-size: 12px; } /style对应的源码逻辑在 handleMenuItemClick()handleMenuItemClick(command, instance) { if (this.hideOnClick) { this.visible false; } this.$emit(command, command, instance); }无论hide-on-click如何取值command事件都会照常触发属性只影响visible的收敛。dropdown.spec.js 中的hide on click用例专门验证了:hide-on-clickfalse时点击菜单项后visible仍为true且command回调收到c。command 事件Evento command点击每个菜单项都会触发一个command事件事件的参数就是该项通过command属性声明的值可以是 string / number / object可用于按动作分支处理逻辑el-dropdown commandhandleCommand span classel-dropdown-link Dropdown Listi classel-icon-arrow-down el-icon--right/i /span el-dropdown-menu slotdropdown el-dropdown-item commandaAction 1/el-dropdown-item el-dropdown-item commandbAction 2/el-dropdown-item el-dropdown-item commandcAction 3/el-dropdown-item el-dropdown-item commandd disabledAction 4/el-dropdown-item el-dropdown-item commande dividedAction 5/el-dropdown-item /el-dropdown-menu /el-dropdown style .el-dropdown-link { cursor: pointer; color: #409EFF; } .el-icon-arrow-down { font-size: 12px; } /style script export default { methods: { handleCommand(command) { this.$message(click on item command); } } } /scriptcommand 事件的组件间通信链路command事件的完整分发链路是理解 Element 组件通信模式的典型样本ElDropdownItemdropdown-item.vue 的handleClick调用this.dispatch(ElDropdown, menu-item-click, [this.command, this])把command值和 item 组件实例作为参数向上派发Emitter mixindispatch由 emitter.js 提供它会沿着$parent链向上遍历直到找到componentName为ElDropdown的祖先组件再把事件emit给该祖先ElDropdowndropdown.vue 在mounted中注册this.$on(menu-item-click, this.handleMenuItemClick)收到后按hideOnClick决定是否收起并向外$emit(command, command, instance)——因此模板上command回调实际接收两个参数command 值与触发它的 ElDropdownItem 实例后者可用于访问该 item 的额外状态。dropdown.spec.js 的menu click用例还验证了command支持对象类型用:commandmyCommandObject绑定{ name: CommandC }后command回调参数完整保留了该对象引用。尺寸Tamaños除默认尺寸外el-dropdown通过size属性提供medium、small、mini三档尺寸作用于触发按钮与菜单项el-dropdown split-button typeprimary Default el-dropdown-menu slotdropdown el-dropdown-itemAction 1/el-dropdown-item el-dropdown-itemAction 2/el-dropdown-item el-dropdown-itemAction 3/el-dropdown-item el-dropdown-itemAction 4/el-dropdown-item /el-dropdown-menu /el-dropdown el-dropdown sizemedium split-button typeprimary Medium el-dropdown-menu slotdropdown el-dropdown-itemAction 1/el-dropdown-item el-dropdown-itemAction 2/el-dropdown-item el-dropdown-itemAction 3/el-dropdown-item el-dropdown-itemAction 4/el-dropdown-item /el-dropdown-menu /el-dropdown el-dropdown sizesmall split-button typeprimary Small el-dropdown-menu slotdropdown el-dropdown-itemAction 1/el-dropdown-item el-dropdown-itemAction 2/el-dropdown-item el-dropdown-itemAction 3/el-dropdown-item el-dropdown-itemAction 4/el-dropdown-item /el-dropdown-menu /el-dropdown el-dropdown sizemini split-button typeprimary Mini el-dropdown-menu slotdropdown el-dropdown-itemAction 1/el-dropdown-item el-dropdown-itemAction 2/el-dropdown-item el-dropdown-itemAction 3/el-dropdown-item el-dropdown-itemAction 4/el-dropdown-item /el-dropdown-menu /el-dropdown尺寸生效的机制是 dropdown.vue 中的计算属性dropdownSize() { return this.size || (this.$ELEMENT || {}).size; }即组件自身size优先未设置时回退到全局配置Vue.prototype.$ELEMENT.sizeVue.use(ElementUI, { size: small })。dropdownSize一方面传给split-button渲染出的两个按钮另一方面通过 dropdown-menu.vue 的inject: [dropdown]从父组件注入并写入菜单根节点的 classel-dropdown-menu--${size}由 dropdown.scss 中各尺寸变体调整菜单项的line-height、font-size与 padding。Dropdown 属性Atributos属性描述类型可选值默认值type菜单按钮的类型参见Button组件仅在split-button为 true 时有效string——size菜单的尺寸同样作用于split-buttonstringmedium / small / mini—split-button是否显示按钮组boolean—falseplacement菜单位置stringtop/top-start/top-end/bottom/bottom-start/bottom-endbottom-endtrigger触发方式stringhover/clickhoverhide-on-click点击菜单项后是否隐藏菜单boolean—trueshow-timeout触发hover时显示下拉菜单前的延迟时间msnumber—250hide-timeout触发hover时隐藏下拉菜单前的延迟时间msnumber—150tabindexDropdown 触发元素的 tabindexHTML 全局属性 tabindexnumber—0disabled是否禁用下拉菜单boolean—false以上默认值与 dropdown.vue 的props定义逐项一致。几点补充说明placement 的实时性dropdown-menu.vue 对dropdown.placement建立了immediate: true的 watcher动态修改 placement 会立即同步到 popper 的currentPlacement无需重建组件。disabled 的双重保险disabled除了在show()/hide()/handleClick()入口处直接拦截外render函数还会把disabled属性注入到自定义触发元素的 attrs 上dropdown.vue使span等原生元素也能呈现禁用态同时菜单插槽在禁用时直接不渲染menuElm disabled ? null : this.$slots.dropdown。测试用例 验证了禁用状态下 hover 不会使visible变为 true。历史属性迁移dropdown.vue 引入Migratingmixin 并声明了menu-align属性已重命名为placement早期版本使用menu-align的用户升级时会收到控制台警告。Dropdown 插槽Slots名称描述—默认Dropdown 的触发元素内容。注意必须是一个有效的 DOM 元素如span、button等或el-component以便挂载 trigger 事件监听器dropdown下拉菜单的内容通常是一个el-dropdown-menu元素Dropdown 事件Eventos名称描述参数click当split-button为true时点击左侧主按钮触发—原生事件对象command点击菜单项时触发由该菜单项发出的 command 值另附第二个参数ElDropdownItem 实例visible-change下拉菜单显示/隐藏时触发显示为 true隐藏为 falsevisible-change的发出点见 dropdown.vue 对visible的 watcher可见性变化时会同时broadcast(ElDropdownMenu, visible, val)通知菜单组件同步showPopper并$emit(visible-change, val)。Dropdown 菜单项属性DropdownMenu Item Atributos作用于el-dropdown-item与 dropdown-item.vue 的props定义一一对应属性描述类型默认值command将发送到 Dropdowncommand回调的值string / number / object—disabled菜单项是否禁用booleanfalsedivided该项上方是否显示分隔线booleanfalseicon图标类名string—实现细节上icon渲染为i :classicon并置于默认插槽之前dropdown.scss 为其设置margin-right: 5pxdivided对应 classel-dropdown-menu__item--divided分隔线由border-top加上一个 6px 高的::before白色遮罩margin: 0 -20px抵消菜单项左右内边距构成见 dropdown.scssdisabled的菜单项设置pointer-events: none且tabindex渲染为null既不可点击也不进入键盘焦点序列。组件结构与渲染原理补充结合三个源码文件Dropdown 的组件协作模型可以概括为ElDropdowndropdown.vue容器组件通过provide() { return { dropdown: this } }向后代注入自身引用负责 props 解析、触发事件绑定、hover 防抖定时器、tabindex/ARIA 管理和command/visible-change/click事件ElDropdownMenudropdown-menu.vue混入 vue-popper 提供绝对定位能力菜单本身是position: absolute的 popper实际坐标由 popper 计算模板上包一层el-zoom-in-top过渡动画v-showshowPopper控制显隐mounted时把自身 DOM 回写到dropdown.popperElm并调用initDomOperation()完成事件与 ARIA 初始化代码注释表明此处还兼容了 Vue 2.6 的v-slot新语法带来的挂载时序差异ElDropdownItemdropdown-item.vue渲染为li classel-dropdown-menu__item点击时经 Emitter mixin 向上传递command。键盘可访问性tabindex属性默认 0并非摆设。initAria() 会为非split-button的自定义触发元素补充rolebutton、tabindex、aria-haspopuplist与aria-controls指向dropdown-menu-${generateId()}动态 id。键盘操作由两套 handler 实现handleTriggerKeyDown()聚焦在触发元素上时↑/↓keyCode 38/40将焦点转移到第一个菜单项Enter/Space13等价于点击触发Tab/Esc9/27收起菜单handleItemKeyDown()聚焦在菜单项上时↑/↓ 在项之间循环移动焦点首尾循环、边界钳制Enter 执行target.click()并按hideOnClick决定是否收起Tab/Esc 收起并归还焦点给触发元素。焦点的移动通过removeTabindex()/resetTabindex(ele)动态改写tabindex非聚焦项一律-1目标项设为0实现focusingwatcher 则会给自定义触发元素临时追加focusingclass配合 dropdown.scss 中的.el-dropdown-selfdefine样式消除键盘聚焦时的多余 outline。测试用例 分别覆盖了触发元素键盘控制Enter 展开、Esc 收起与菜单项键盘导航↓ 移动焦点、Enter 选中并收起。版本与使用前提本文内容基于当前仓库源码适用于 Elementelement-uiVue 2.x 版本线Vue 3 用户对应的实现位于 Element PlusAPI 大体延续但源码路径与细节不同不在本文讨论范围内组件注册入口见 packages/dropdown/index.jsElDropdown.install仅全局注册ElDropdownel-dropdown-menu与el-dropdown-item由ElDropdown.install之外的主入口 src/index.js 统一注册TypeScript 类型声明参见 types/dropdown.d.ts其属性命名splitButton、hideOnClick等与模板 kebab-case 属性一一对应。【免费下载链接】elementA Vue.js 2.0 UI Toolkit for Web项目地址: https://gitcode.com/gh_mirrors/eleme/element创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考