前言
本文所列封装组件的步骤,是以 Element UI 的回到顶部组件为例,但不完全局限于它(至少可以做个参考)。所展示的 demo 也做了一点修改。
封装步骤
以下回答仅是个人对 vue 组件封装的一些见解,不一定全对,若有不妥之处,还请谅解。
1. 明确将要封装的组件是装饰性的组件还是功能性的组件?
这个问题很好理解,说白了就是你将要封装的组件是用来做啥的。那么关于本文章的回到顶部按钮是一个什么组件呢?答案很明显,它是一个功能性的组件,因为设计它的目的:是为了让用户通过点击来使滚动条从页面从底部回到顶部。
2. 场景分析
就是明确你所封装的组件需要用在什么地方?以回到顶部按钮为例,那么用到它的主要场景:就是页面内容较长,需要向下滚动浏览,且回到顶部时,可以一步到位(不用费手指地滑鼠标向上滚)。
3. 状态分析
这一步骤,一般多用于封装装饰性组件。例如,按钮组件,它一般有如下几个状态:
- 默认状态
- 成功状态
- 信息状态
- 警告状态
- 危险状态
就回到顶部这种按钮形式的组件而言,我们只需一个默认状态(也就是初始化)。例如,距离页面底部或右边距的距离是多少,滚动高度为多少时显示按钮等。
4. 形态分析
主要指你封装的组件展示出来是什么样子(也就是 css 样式)。一般而言,按钮组件都会有一些形状样式和动画效果以供选择,例如:
- 圆形
- 正方形
- 淡入淡出过渡动画
而我们的回到顶部按钮组件,则打算通过 slot(插槽)的方式让开发者自行设计,这样的组件样式就不会只固定为有限的几种,而是姹紫嫣红。
5. 尺寸大小
通常情况下,按钮形态的组件都会有几个不同的尺寸以供选择。
- small —— 小
- medium —— 中等
- large —— 大
由于打算让开发者能够自定义,所以这里也就不必刻意设计可选的大小。
6. 事件监听函数
在封装 vue 组件时,一般情况下,都会给其设置一些事件监听函数以便实现某些操作。
7. 暴露对外属性和事件
通过上述的一系列分析,我们可能会暴露出以下属性和事件监听函数。
属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
target | 触发滚动的对象 | string | |
visibility-height | 滚动高度达到此参数值才出现 | number | 200 |
right | 控制其显示位置, 距离页面右边距 | number | 40 |
bottom | 控制其显示位置, 距离页面底部距离 | number | 40 |
transition-name | 过渡动画效果 | string | el-fade-in |
事件
参数 | 说明 | 回调参数 |
---|---|---|
click | 点击按钮触发的事件 | 点击事件 |
代码实现
注意,整个组件除了为其增添了一个 transition-name
过渡动画属性以外,再无其它更改。
HTML 结构布局
<template>
<transition :name="transitionName">
<div v-if="visible"
:style="{
'right': styleRight,
'bottom': styleBottom
}"
class="el-backtop"
@click.stop="handleClick"
>
<!-- 插槽中的默认样式,可以根据个人喜好更换,这需要在使用组件时传入 -->
<slot>
<el-icon name="caret-top" />
</slot>
</div>
</transition>
</template>
js 代码
代码中引入的 throttle(节流)函数,主要是防止用户不断地重复点击按钮触发滚动函数。大家可以通过 npm install throttle-debounce --save
在项目中安装引入,也可以像我在代码中写的一样:通过文件方式引入,前提是你得先下载(本人准备的 demo 中就有,需要的可以自取)。若是,同学们对节流防抖感兴趣的话,不妨看看我写的相关文章。
<script>
// 节流,确保一定时间段内只会调用一次事件处理函数。防止用户重复性点击。
import throttle from './js/throttle'
// 设置滚动动画效果
const cubic = value => Math.pow(value, 3)
const easeInOutCubic = value => value < 0.5
? cubic(value * 2) / 2
: 1 - cubic((1 - value) * 2) / 2
export default {
name: 'ElBacktop',
props: {
// 滚动高度
visibilityHeight: {
type: Number,
default: 200
},
// 触发滚动的对象
target: [String],
// 页面右边距距离
right: {
type: Number,
default: 40
},
// 页面底部距离
bottom: {
type: Number,
default: 40
},
// 过渡动画
transitionName: {
type: String,
default: 'el-fade-in'
}
},
data() {
return {
el: null, // 触发滚动的对象
container: null,
visible: false // 控制组件显示
}
},
computed: {
// 利用计算属性,监听 bottom 和 right 的值
styleBottom() {
return `${this.bottom}px`
},
styleRight() {
return `${this.right}px`
}
},
mounted() {
// 初始化
this.init()
// 生成节流函数并返回
this.throttledScrollHandler = throttle(300, this.onScroll)
// 监听 scroll 事件
this.container.addEventListener('scroll', this.throttledScrollHandler)
},
// 销毁钩子
beforeDestroy() {
// 移除 scroll 事件
this.container.removeEventListener('scroll', this.throttledScrollHandler)
},
methods: {
// 初始化,主要是获取滚动的对象
init() {
this.container = document
this.el = document.documentElement
if (this.target) {
this.el = document.querySelector(this.target)
if (!this.el) {
throw new Error(`target is not existed: ${this.target}`)
}
this.container = this.el
}
},
// 监听滚动值,用于显示和隐藏回到顶部按钮组件
onScroll() {
const scrollTop = this.el.scrollTop
this.visible = scrollTop >= this.visibilityHeight
},
// 点击事件
handleClick(e) {
this.scrollToTop()
this.$emit('click', e) // 触发父级监听事件函数 click
},
// 滚动函数
scrollToTop() {
const el = this.el // 滚动对象
const beginTime = Date.now() // 开始滚动的时间
const beginValue = el.scrollTop // 滚动距离
// 执行动画
const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16))
// 更新动画的回调函数
const frameFunc = () => {
// 控制动画进度
const progress = (Date.now() - beginTime) / 500
// progress 大于 1,则滚动动画执行完成,滚动条回到顶部
if (progress < 1) {
el.scrollTop = beginValue * (1 - easeInOutCubic(progress))
rAF(frameFunc)
} else {
el.scrollTop = 0
}
}
// 回调,持续执行滚动动画
rAF(frameFunc)
}
}
}
</script>
使用方式
elementui 的回到顶部组件是没有 transition-name
过渡动画属性的,这里我稍作更改,为其添加了上去(完全照搬,太不像话,就加了一个,嘿嘿...)。
// 方式一
<BackTop :bottom="100" transition-name="el-fade-in-linear">
<div
style="{
height: 100%;
width: 100%;
background-color: #f2f5f6;
box-shadow: 0 0 6px rgba(0,0,0, .12);
text-align: center;
line-height: 40px;
color: #1989fa;
}"
>
UP
</div>
</BackTop>
// 方式二
<BackTop :bottom="100" transition-name="el-fade-in-linear"></BackTop>
最后
文章并没有什么深邃的东西值得探讨,主要是为和同学们分享我个人封装 vue 组件的一些感悟。若有不妥之处,还请担待。