Quasar Bottom Sheet 插件完全指南:从列表/网格动作面板到源码级关闭原理

发布时间:2026/9/20 23:42:10
Quasar Bottom Sheet 插件完全指南:从列表/网格动作面板到源码级关闭原理
Quasar Bottom Sheet 插件完全指南从列表/网格动作面板到源码级关闭原理【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址: https://gitcode.com/gh_mirrors/qu/quasarBottom Sheet底部动作面板是 Quasar Framework 内置的一个 UI 插件它从设备屏幕底部滑出展示一组可供用户确认或取消的操作选项。本文以官方文档 docs/src/pages/quasar-plugins/bottom-sheet.md 为主体结合仓库源码插件实现、组件渲染、核心调度器展开读完你将掌握BottomSheet.create()/$q.bottomSheet()的完整用法、全部可配置参数、链式回调 API以及点背景关闭、按 ESC 关闭、路由跳转关闭等行为背后的实现原理。什么是 Bottom SheetBottom Sheet 从设备屏幕的底部边缘向上滑出显示一组操作选项并支持确认或取消某个动作。它与菜单Menu在定位上有微妙区别可以作为菜单的替代方案当需要在一组并列选项中快速选择时Bottom Sheet 的触达面积更大、更符合移动端手势习惯不应用于导航它只适合承载动作如分享、上传、删除确认不适合承担页面路由级别的导航职责。Bottom Sheet 始终悬浮在页面其他所有组件之上必须被关闭后才能与底层的页面内容交互。触发时页面其余部分会变暗出现半透明遮罩将视觉焦点集中到面板选项上。它支持两种展示形态列表List纵向排列适合条目较多、文本较长的场景网格Grid横向按行排布适合图标化的快捷动作。同时每个动作项既可以配图标icon也可以配头像avatar或图片img。在 Quasar 中Bottom Sheet 有两种使用方式在 Vue 模板中作为组件或者作为全局可用方法插件形式。安装与引入Bottom Sheet 是 Quasar 官方插件通过 ui/src/plugins.js 导出export { default as BottomSheet } from ./plugins/bottom-sheet/BottomSheet.js在quasar.config.js中启用插件无需额外安装 npm 包随 Quasar 框架一起提供// quasar.config.js framework: { plugins: [BottomSheet] }安装后Quasar 会在全局注入$q.bottomSheet方法。其底层实现非常精简见 ui/src/plugins/bottom-sheet/BottomSheet.jsimport BottomSheet from ./component/BottomSheetComponent.js import { createDialog } from ../../utils/private.dialog/create-dialog.js export default { install({ $q, parentApp }) { $q.bottomSheet this.create createDialog(BottomSheet, false, parentApp) } }可以看到插件安装时把BottomSheet.create与$q.bottomSheet绑定为同一个函数引用二者等价。createDialog(BottomSheet, false, parentApp)中的第二个参数false表示该插件不支持自定义组件对比 Dialog 插件始终渲染内置的BottomSheetComponent。提示在模板中使用组件形式时对应的组件类名为BottomSheetComponent内部名称官方推荐的公开使用方式仍是插件方法因为插件方法会自动挂载到全局 DOM 节点并管理生命周期。基本用法编程式调用在 Vue 文件之外例如纯 JS 模块、路由守卫、Store 中使用BottomSheet.create在 Vue 文件内部使用useQuasar()注入的$q.bottomSheet。二者返回同一个 Promise 风格的链式对象。// 在 Vue 文件之外 import { BottomSheet } from quasar BottomSheet.create({ ... }) // 返回 Object链式 API // 在 Vue 文件内部 import { useQuasar } from quasar setup () { const $q useQuasar() $q.bottomSheet({ ... }) // 返回 Object链式 API }官方文档给出的完整实战示例对应 docs/src/examples/BottomSheet/Basic.vue如下script setup import { useQuasar } from quasar const $q useQuasar() function show(grid) { $q.bottomSheet({ message: Bottom Sheet message, grid, actions: [ { label: Drive, img: https://cdn.quasar.dev/img/logo_drive_128px.png, id: drive }, { label: Keep, img: https://cdn.quasar.dev/img/logo_keep_128px.png, id: keep }, { label: Google Hangouts, img: https://cdn.quasar.dev/img/logo_hangouts_128px.png, id: calendar }, { label: Calendar, img: https://cdn.quasar.dev/img/logo_calendar_128px.png, id: calendar }, {}, // 空对象 在列表/网格中渲染为分隔符Separator { label: Share, icon: share, id: share }, { label: Upload, icon: cloud_upload, color: primary, id: upload }, {}, // 分隔符 { label: John, avatar: https://cdn.quasar.dev/img/boy-avatar.png, id: john } ] }) .onOk(action { console.log(Action chosen:, action.id) }) .onCancel(reason { // reasonQuasar v2.28取值backdrop、escape 或 programmatic console.log(Dismissed:, reason) }) .onDismiss(() { console.log(I am triggered on both OK and Cancel) }) } /script示例中值得注意的两个细节空对象{}作为分隔符当某个 action 对象没有label时源码 BottomSheetComponent.js 会将其渲染为QSeparator组件在列表模式中渲染QSeparator带间距在网格模式中渲染为占满整行的col-all分隔条action 支持任意自定义属性如示例中的idonOk回调会把整个 action 对象原样回传因此自定义字段如id、业务数据可以在回调中直接使用。链式回调 APIcreate()/$q.bottomSheet()返回的链式对象来自 create-dialog.js支持以下方法均可链式调用方法触发时机回调参数onOk(fn)用户点击某个动作被点击的整个 action 对象onCancel(fn)面板被取消关闭未选择任何动作关闭原因backdrop/escape/programmaticonDismiss(fn)无论确认还是取消只要面板关闭都会触发若未选动作则传关闭原因否则传 action 对象hide()主动关闭面板—update(props)热更新面板的配置如标题、actions、dark 等—实现细节onDismiss本质上是同时注册ok与cancel两套回调create-dialog.jshide()与update()通过遍历组件树查找show/hide方法并调用兼容script setup中组件可能被异步包装的情况create-dialog.js。当面板关闭且未触发 OK 时onHide会把 dismiss reason 传给所有 cancel 回调create-dialog.js。此外在 SSR服务端渲染环境下create返回一个安全的空实现 API所有方法均为空操作并返回自身避免服务端直接操作 DOMcreate-dialog.js。完整参数说明API 全解依据官方 API 定义文件 ui/src/plugins/bottom-sheet/BottomSheet.jsonBottomSheet.create(opts)接受以下顶层选项参数类型默认值说明titleString—面板标题messageString—面板说明文字actionsArray—动作数组每个元素是一个对象gridBooleanfalsetrue时以网格展示否则为列表darkBoolean—强制应用暗色模式默认跟随全局seamlessBooleanfalse无缝模式不使用遮罩用户可与页面其余部分交互persistentBooleanfalse持久模式点击外部、按 ESC 均无法关闭路由变化也不会关闭它classes即classString/Array/Object—应用于面板卡片QCard的 CSS 类styleString/Array/Object—应用于面板卡片的内联样式提示class与style在传入插件时会被内部转换为cardClass/cardStylecreate-dialog.js最终作用于 BottomSheetComponent.js 中的QCard上。actions 数组中每个动作对象的字段字段类型说明labelString / Number动作的文本标签缺省时该对象被渲染为分隔符iconString图标名称需配合已安装的图标集使用colorString图标颜色透传给 QIcon 的colorpropimgString动作图片路径支持public 目录路径如img/something.png、相对路径如:srcrequire(./my_img.jpg)、远程 URLavatarString头像图片路径与img支持相同格式渲染为圆形头像样式q-bottom-sheet__avatarclassesString/Array/Object该动作元素的 CSS 类styleString/Array/Object该动作元素的内联样式...Any任意其他自定义属性会在onOk回调中原样返回优先级规则见 BottomSheetComponent.js 的getGrid/getListicon优先于img/avatar——若配置了icon则渲染QIcon否则才尝试渲染img或avatar图片。关闭行为与关闭原因Dismissal Reason官方文档明确了两条平台行为在Cordova 应用中用户点击手机/平板系统的返回键Bottom Sheet 会自动关闭在桌面浏览器中按下ESCAPE键同样会关闭。此外从Quasar v2.28 开始onCancel回调以及未选择任何动作时的onDismiss会接收到关闭原因的字符串取值有三种取值含义backdrop用户点击了面板外部的半透明遮罩区域escape用户按下了 ESC 键programmatic通过代码主动关闭调用hide()也包括应用发生路由跳转导致的自动关闭该原因字符串由 dismiss-reason.js 生成逻辑非常直白export function getDismissReason(evt) { return evt void 0 ? programmatic : evt.type.indexOf(key) 0 ? escape : backdrop }即无事件对象 →programmatic键盘事件keydown/keyup→escape其余鼠标/触摸点击遮罩→backdrop。测试用例 BottomSheet.test.js 中有专门的dismissal reason测试分组验证这三种原因的分发逻辑。背后的组件实现一切基于 QDialog从源码结构看Bottom Sheet 并没有另起炉灶而是复用 Quasar 的 QDialog 组件作为容器BottomSheetComponent的根节点就是一个QDialog并固定设置position: bottomBottomSheetComponent.js。QDialog 的position为bottom时会应用fixed-bottom justify-center定位类并使用slide-up显示/slide-down隐藏的默认过渡动画QDialog.js这正是从底部滑出效果的来源。底层关闭行为与参数的关系QDialog.jsconst hideOnRouteChange computed( () !props.persistent !props.noRouteDismiss !props.seamless )点击遮罩关闭仅当非persistent、非noBackdropDismiss时生效QDialog.jsESC 关闭仅当非seamless、非persistent、非noEscDismiss时生效QDialog.js路由变化关闭由上述hideOnRouteChange计算属性控制。因此seamless: true时连遮罩都不渲染useBackdrop为false外层类切换为q-dialog--seamless见 QDialog.js用户可以直接与页面交互persistent: true时遮罩、ESC、路由跳转三种关闭途径全部被禁用只能通过点击动作或代码hide()关闭。强制暗色模式Force dark mode官方文档第二个示例展示如何在 Bottom Sheet 上强制应用暗色模式对应 docs/src/examples/BottomSheet/Dark.vue只需传入dark: true$q.bottomSheet({ dark: true, message: Bottom Sheet message, grid, actions: [ /* ... */ ] })源码中dark来自useDarkProps混入BottomSheetComponent.js通过useDark(props, useQuasar())计算最终是否处于暗色状态。当为暗色时面板容器类追加q-bottom-sheet--dark q-darkBottomSheetComponent.js列表项QItem与分隔符QSeparator均传入dark: isDark()保证子元素配色统一若未传dark则自动跟随全局暗色主题。源码级原理小结一次调用的完整生命周期综合插件层、组件层与调度层源码一次$q.bottomSheet({...})调用的完整链路如下插件层$q.bottomSheet即createDialog(BottomSheet, false, parentApp)返回的工厂函数BottomSheet.js调度层createDialog在 SSR 下返回空实现浏览器端解析传入的 optionsclass/style转换为cardClass/cardStyle通过createChildApp创建名为QGlobalDialog的挂载子应用并插入到全局 DOM 节点create-dialog.js组件层BottomSheetComponent依据title/message/actions/grid/dark构建卡片内容——列表模式用QItemQItemSection网格模式用div.row布局无label的项渲染为QSeparatorBottomSheetComponent.js交互层点击动作触发onOk(action)并关闭点击遮罩 / 按 ESC / 路由跳转 / 代码hide()则触发onHide(getDismissReason(evt))将关闭原因分发给onCancel/onDismiss回调dismiss-reason.js、create-dialog.js清理层面板隐藏后卸载子应用、移除全局 DOM 节点避免内存泄漏create-dialog.js。单元测试 ui/src/plugins/bottom-sheet/BottomSheet.test.js 验证了$q.bottomSheet注入正确性、create()可调用且返回对象 API、网格模式正确渲染.q-bottom-sheet--grid节点、动作标签文本出现在 DOM 中、hide()后面板节点被移除以及三种 dismissal reason 的分发组件级测试 ui/src/plugins/bottom-sheet/component/BottomSheetComponent.test.js 则覆盖了列表/网格渲染与分隔符行为。总结Bottom Sheet 是 Quasar 中处理底部动作选择的标准答案一行$q.bottomSheet({ actions, grid, dark })即可获得带遮罩、动画、键盘/返回键支持的动作面板配合onOk/onCancel/onDismiss链式回调可以精细控制业务流转。其内部复用 QDialog 与全局子应用挂载机制seamless、persistent两个开关分别对应可交互页面与不可被外部关闭两种极端场景dark参数则可与全局主题无缝协同。完整的选项清单以官方 API 文档 ui/src/plugins/bottom-sheet/BottomSheet.json 为准仓库内的 Basic.vue 与 Dark.vue 是两个可以直接复制运行的最小示例。【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址: https://gitcode.com/gh_mirrors/qu/quasar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考