前言
在日常开发中,button组件是使用频率比较高的组件,原生的HTML button标签只能满足一部分的使用场景,所以需要对button标签进行二次封装,来满足更多的场景。
设计
对于一个button组件,我们对它做如下设计:
- 保留原生HTML button标签的部分特性 (如:type, disabled等)
- 通过传入type prop,来展示不同类型的button
- 通过传入loading prop,来控制button的加载效果
- 通过传入icon prop,实现button和icon的结合
- 通过传入ripple prop,实现水波纹的点击效果
- 通过传入block prop,来控制button的宽度是否继承父容器宽度
- 提供自定义的click事件,来响应用户的点击
具体设计如下所示:
实现
组件代码如下所示:
1 | <template> |
提示:不同的浏览器对
<button>
元素的 type 属性使用不同的默认值, 所以我们需要给自定义的button 组件的 htmlType 属性设置默认的值为button
从上可知button组件提供了2个 slot
:
- 默认插槽一般用于接收button组件的文本传入;
- icon 插槽,提供给用户插入图片的途径(当icon图标不能够完全满足需求时,我们可以传入图片代替)
由于它没有复杂的逻辑,基本上就是一个视图组件,接下来我们将重点分析它的样式,对应的css样式(stylus语法)代码 如下所示:
1 | $text-color = #4a4a4a |
分析上述重点样式代码:
- 设置
-webkit-appearance: none
去除 button 标签默认的appearance样式,常用于IOS下移除原生样式 - 设置
outline: none
去除外边线样式 - 设置
touch-action: manipulation
可减少浏览器在用户点击时延迟生成点击事件的情况 - 设置
white-space: nowrap
禁止组件文本换行 - 设置
user-select: none
阻止button组件文本被选择(复制) - 设置
-webkit-text-size-adjust: 100%
关闭字体大小自动调整功能
还需要注意的是,当点击button组件时,需要设置active状态下的样式,如果直接在button上改变 background-color
或者其它样式的值,这样将会导致额外的 layout 和 paint, 如下所示:
为了能够进行性能优化,做如下设计:
- 将 button 组件 active 状态下的样式通过在 ‘::before’ 上设置绝对定位进行触发,这样点击button时就不会影响到其他dom的渲染
- 为了不影响button的正常点击操作,需要在伪元素上设置
pointer-events: none
这样点击事件就会穿透到真正到button元素上 - 通过改变
opacity
样式来反馈 active 状态 - 为了做更近一步的优化,我们通过设置
transition
样式将伪元素提升为合成层 (元素提升为合成层后,transform 和 opacity 才不会触发 paint,如果不是合成层,则其依然会触发 paint)
提示:(这里是引用 justjavac 大神的回答)
在 Blink 和 WebKit 内核的浏览器中,对于应用了 transition 或者 animation的 opacity 元素,浏览器会将渲染层提升为合成层。也可以使用 translateZ(0) 或者 translate3d(0,0,0) 来人为地强制性地创建一个合成层。
可以通过使用 Chrome DevTools 工具来查看页面中合成层的情况,在 Rendering
标签下,勾选上 Show layer borders即可
✨请点击查看 DEMO 效果
附录 ✨
- touch-action介绍
- pointer-events运用场景
- Spin组件分析
- Ripple组件分析
- b函数分析
- CSS Triggers 文档
- 对于改变opacity样式时,页面渲染的疑惑