VueJS 2/Vuetify 2封装v-bottom-sheet组件无法修改v-model问题
Vue2 + Vuetify2 封装带默认关闭按钮的v-bottom-sheet问题解析
问题背景
为方便后续Vue和Vuetify框架升级,需要封装一个自带默认关闭按钮的v-bottom-sheet组件,但初期实现时关闭按钮点击无响应,无法关闭底部面板,尝试两种方案均失败后最终解决,现询问该封装是否存在过度设计。
失败方案解析
方案1:仅使用v-bind="$attrs"和v-on="$listeners"
<template> <v-bottom-sheet v-bind="$attrs" v-on="$listeners"> <v-sheet> <v-btn @click="onClick">X</v-btn> <slot></slot> </v-sheet> </v-bottom-sheet> </template> <script> import { VBottomSheet } from 'vuetify/lib'; import { VSheet } from 'vuetify/lib'; import { VBtn } from 'vuetify/lib'; export default { name: 'BaseBottomSheet', props: { scrollable: { type: Boolean, default: false }, }, methods: { onClick(value) { console.log("V", value) } } }; </script>
失败原因:
- 未处理
v-model双向绑定逻辑:父组件用v-model="openSheet"绑定状态,但子组件未接收value属性,也未触发input事件更新父组件状态。 - 关闭按钮的
onClick仅打印日志,没有执行关闭面板的核心逻辑。
方案2:绑定value但事件参数错误
<template> <v-bottom-sheet :value="value" v-on="$listeners"> <v-sheet> <v-btn :value=value @click="$emit('input', $event)">X</v-btn> <slot></slot> </v-sheet> </v-bottom-sheet> </template> <script> import { VBottomSheet } from 'vuetify/lib'; import { VSheet } from 'vuetify/lib'; import { VBtn } from 'vuetify/lib'; export default { name: 'BaseBottomSheet', props: { scrollable: { type: Boolean, default: false }, value: Boolean } }; </script>
失败原因:
- 点击按钮时
$emit('input', $event)传递的是点击事件对象,而非v-model需要的布尔值(!value),导致父组件的openSheet被赋值为事件对象,无法正确切换面板状态。 - 未通过
v-bind="$attrs"传递额外属性,可能丢失原v-bottom-sheet的部分配置能力。
成功方案说明
<template> <v-bottom-sheet :value="value" v-bind="$attrs" v-on="$listeners"> <v-sheet> <v-btn :value="value" @click="onClick">X</v-btn> <slot></slot> </v-sheet> </v-bottom-sheet> </template> <script> import { VBottomSheet } from 'vuetify/lib'; import { VSheet } from 'vuetify/lib'; import { VBtn } from 'vuetify/lib'; export default { name: 'BaseBottomSheet', props: { scrollable: { type: Boolean, default: false }, value: { type: [Boolean, PointerEvent] } }, methods: { onClick(value) { this.$emit('input', !this.value); } } }; </script>
解决核心:
- 正确接收
value属性,并兼容Boolean和PointerEvent类型(适配Vuetify内部触发的事件参数)。 - 关闭按钮点击时,通过
$emit('input', !this.value)传递正确的布尔值,触发父组件v-model的状态切换。 - 保留
v-bind="$attrs"和v-on="$listeners",确保原v-bottom-sheet的所有属性、事件能正常传递,不丢失原有功能。
封装过度判断与优化建议
是否封装过度?
当前封装不算过度:
- 仅在原
v-bottom-sheet基础上添加了默认关闭按钮,无额外冗余逻辑。 - 通过
$attrs和$listeners完整保留了原组件的API能力,后续框架升级时,只要v-bottom-sheet的核心API(value/input事件)变化不大,封装组件几乎无需修改,完全符合“便于升级”的需求。
优化建议
- 移除单独定义的
scrollable属性:该属性可通过$attrs自动传递给v-bottom-sheet,无需单独声明,减少代码冗余。 - 简化关闭按钮逻辑:直接在模板中写
@click="$emit('input', !value)",无需额外定义onClick方法。 - 给
value属性添加默认值:避免未传递v-model时出现异常,比如default: false。
优化后的代码示例:
<template> <v-bottom-sheet :value="value" v-bind="$attrs" v-on="$listeners"> <v-sheet> <v-btn @click="$emit('input', !value)">X</v-btn> <slot></slot> </v-sheet> </v-bottom-sheet> </template> <script> import { VBottomSheet, VSheet, VBtn } from 'vuetify/lib'; export default { name: 'BaseBottomSheet', props: { value: { type: [Boolean, PointerEvent], default: false } }, components: { VBottomSheet, VSheet, VBtn } }; </script>
内容的提问来源于stack exchange,提问作者Sarah
相关产品推荐
相关产品推荐

