制作 Button 组件

[toc]
需求分析
- 借鉴 AntD/ Bulma/Eleme/iView/Vuetify 等
- 需求
- 可以有不同的等级
- 可以是链接,可以是文字
- 可以 click/focus/鼠标悬浮
- 可以设置size
- 可以设置状态为禁用
- 可以设置状态为加载中
API 设计
|
|
创建 Button 组件
Button 基础样式
Button.vue
|
|
ButtonDemo.vue
|
|
- 使用
<slot></slot>来让UI库用户自定义button内部的结构
让Button支持事件
Button.vue
|
|
- vue自动将绑定的事件传到子组件中根节点上
- 不用再处理事件代理逻辑
这样会带来一个问题:点击外层元素,而未点击到button元素上也会触发事件
|
|
让div不继承属性
- 使用
inheritAttrs: false来禁用 Attribute 继承,。偶人为true - 此时事件没有绑在子组件任何一个元素上
Button.vue
|
|
- 参考vue文档 非 Prop 的 Attribute
Vue 3 属性绑定细节
让div里的button显式地绑定$attrs
- 在父组件/元素上写
$attrs,实现所有属性的祖传孙元素 $attrs会以对象的形式展示所有祖元素/组件上的属性- 使用
v-bind="$attrs",此处不可缩写
Button.vue
|
|
- 小结:让组件内部某一个节点拥有外部(使用该组件时)传入的(写在标签上的)所有属性,而其他节点不同时拥有的情况
让div继承一部分属性,让button继承另一部分属性
- 在
setup中使用context.attrs将属性结构出来 - 在
script setup语法糖中,使用useAttrs()获取$attrs来处理- 解构赋值取出属性
const {size, ...rest} = attrs; - 在外层div上绑定
<div :size="size">...</div> - 在button上绑定其他剩余属性
<button v-bind="rest"><slot></slot></button>
- 解构赋值取出属性
Button.vue
|
|
小结
Vue3属性绑定- 默认所有属性都绑定到根元素
- 使用
inheritAttrs: false可以取消默认绑定 - 使用
$attrs或者context.attrs获取使用该组件时绑定的所有属性- 单文件组件 语法糖中由
const attrs = useAttrs();获取
- 单文件组件 语法糖中由
- 使用
v-bind="$attrs"批量绑定属性 - 使用
const {size, ...rest} = context.attrs将属性解构分开- 单文件组件 语法糖中为
const {size, ...rest} = attrs;
- 单文件组件 语法糖中为
实现让button支持theme属性
Button.vue添加属性theme的可选值为button/link/text
|
|
- 给
button添加默认样式类vue-button - 动态绑定样式类
:class="`theme-${theme}`" - 外部数据props添加默认值
const props = withDefaults(defineProps<Props>(), { theme: 'button' });
ButtonDemo.vue
|
|
添加button组件样式
|
|
UI库组件的CSS 的两个注意事项
- 不能使用
scoped- 因为
data-v-xxx中的xxx每次运行可能不同 - 必须输出稳定不变的
class选择器,方便使用者覆盖
- 因为
- 必须加前缀
.vue-buttonV.S..button.vue-theme-linkV.S..theme-link
CSS最小影响原则
|
|
CSS不能影响库使用者
- 使用较特殊的前缀定义样式,重构
Button.vue
|
|
完成 Button 组件
让Button支持theme属性
theme的值为button/link/text 默认为button
|
|
让Button支持size属性
size的值为big/normal/small
|
|
- 使用计算属性代替
:class="vue-theme-${theme}"中的表达式,使得代码更清晰
|
|
让button支持level属性
level的值为 main/normal/danger
|
|
让button支持disabled属性
disabled的值为 true/false 默认为false
-
<button disabled> -
<button :disabled="true"> -
<button disabled="true">错误 -
<button disabled="false">错误
|
|
- 如果已经声明外部属性props中的disabled,就不会被继承
- 需要手动写明
<button :disabled="disabled">绑定属性
让button支持loading属性
loading的值为 true/false
|
|
知识点小结
- Vue属性继承
- 默认属性传给根元素
inheritAttrs: false禁用默认继承v-bind="$attrs"或context.attrs
propsV.S.attrsprops需要显式地声明属性- 未显示声明的属性都在
attrs中
- 库的CSS要求
- 不可用scoped
- 每个 CSS 类要加前缀
- CSS最小影响原则
UI参考