组件是 Vue 的一个极其重要的概念。在移动端网页开发时,Toast 组件使用也是非常频繁的。本文便以 Toast 组件为例,来讲解 Vue 组件的部分知识点。
1. 单文件组件
日常开发时,我们项目文件夹通常都是使用 vue-cli 创建的,以 单文件组件 的方式来组织代码的。按照平日的开发流程,现在先创建一个 Toast.vue 文件。
我们开始关注实现 Toast 组件的一些要点:
- 该组件有两个 prop : visible 与 msg。visible 控制显示与隐藏,msg 是显示的内容
- 该组件的位置: 相对于屏幕视口来确定位置,position 设置为 fixed,并且把 z-index 值设置大一点。为了让元素居中显示,要用到 translate 属性
- 该组件显示或者隐藏时添加了动画: 使用 transition 组件来实现过渡效果
根据上文的分析,现在的代码如下。
<template>
<transition name="toast-bounce">
<div class="toast" v-show="visible">
<p class="toast-msg">{{ msg }}</p>
</div>
</transition>
</template>
<script>
export default {
name: 'Toast',
props: {
visible: {
type: Boolean,
required: true
},
msg: {
type: String,
required: true
}
}
}
</script>
<style lang="scss" scoped>
// 添加过渡效果
.toast-bounce-enter, .toast-bounce-leave {
opacity: 0;
}
.toast {
box-sizing: border-box;
position: fixed;
max-width: 80%;
left: 50%;
top: 50%;
padding: 20px;
z-index: 99;
transition: all .3s ease;
transform: translate(-50%, -50%);
border-radius: 10px;
background: rgba(0, 0, 0, 0.7);
color: #fff;
text-align: center;
.toast-msg {
text-align: center;
}
}
</style>
在需要使用 Toast 组件的页面,引入该组件,注册之后就能使用了。
2. 如何手动创建组件
以单文件组件引入,有几个不方便的地方。
- Toast 每次使用都要导入
- 父组件的 state 要包含一个 visible 的属性 (假设我们没有使用mixin)
- 设置 父组件的 visible 为 true 之后,用一个定时器再把 visible 设置为 false
类似这样使用频率较高的组件,最便捷的方式是通过调用一个函数来完成组件的创建,挂载以及销毁。但而在开发项目过程中,我们把精力放在了组件的实现上,忽略了一些这些操作。接下来我们将演示手动如何创建 Toast 组件,附加到 document.body 下,在显示一段时间后,销毁组件,再移除该元素。
组件的创建与挂载
Vue 单文件的 script 标签仅仅是导出一个包含组件选项的对象。为了创建组件,先使用 Vue.extend 得到一个构造器,接下来通过 new 进行实例化。这里的构造函数有一个可选参数,类型是 Object,可以传入的属性有 el,propsData等。
import ToastConfig from './Toast.vue'
// 构造器
const ToastConstructor = Vue.extend(ToastConfig)
// 通过 new 创建组件
const instance = new ToastConstructor()
Toast 组件在创建时,两个 prop: visible, msg 必须要传入。在 new 实例化时,配置 propsData 参数来设置组件所需的 prop。
const instance = new ToastConstructor({
propsData: {
msg: 'msg',
visible: false
}
})
组件创建与挂载是两个不同的概念,组件创建时,挂载阶段还没开始,DOM 元素中不含该节点。在 挂载 了之后,才会被浏览器渲染。
在创建实例时,不但要设置 propsData属性,还要设置挂载点 el 属性,并且将组件附在 document.body 之后。
const instance = new ToastConstructor({
el: document.createElement('div'),
propsData: {,
msg: 'msg',
visible: false
}
})
// 组件创建成功之后挂载
document.body.appendChild(instance.$el)
挂载之后,显示 toast 组件。再开启一个定时器,在 3s 之后隐藏组件。
Vue.nextTick(() => {
instance.visible = true
setTimeout(() => {
instance.close()
}, 3000)
})
组件的销毁
Toast 组件隐藏的动画结束时,触发 transitionend 事件。instance.$el 返回组件的根元素,监听该元素的 transitionend 事件,在回调函数中 销毁 组件,同时从DOM树中移除。
// 在 ToastConstructor 上添加两个销毁组件与移除DOM元素的函数
// 隐藏组件
ToastConstructor.prototype.close = function () {
this.visible = false
this.$el.addEventListener('transitionend', this.destroyeInstance.bind(this))
}
// 销毁组件,移除DOM元素
ToastConstructor.prototype.destroyeInstance = function () {
this.$destroy(true)
this.$el.removeEventListener('transitionend', this.destroyeInstance)
this.$el.parentNode.removeChild(this.$el)
}
完整代码片段点击 这里。
3. 小的调整
如果在短时间频繁调用 toast 函数,document.body 下面存在多个 toast 组件,后一个会覆盖前一个。考虑到移动端的特点,我们希望在同一个时间点上,不论 toast 函数调用有多频繁,document.body 只有会有一个 toast 组件。
接下来的调整思路大致如下:
- 检测是否存在 Toast 组件
- 不存在,创建即可
- 存在的话,重新设置 state 中的 visible 与 msg,移除销毁组件的定时器,移除对 transitionend 时间的监听
代码需要进行一些的调整。
// 在函数之外,定义两个变量
// 组件销毁时,instance 也要置为 null
let instance = null
let timer = null
function toast (msg = '默认信息') {
// 判断 instance 是否存在
if (instance) {
instance.visible = true
instance.msg = msg
if (timer) {
clearInterval(timer)
}
instance.$el.removeEventListener('transitionend', instance.destroyeInstance)
} else {
//...
}
// ...
}
完整代码片段点击 这里。
当然我们也可以再更进一步,把 toast 函数注册为 Vue 插件,在需要的地方通过 this.$toast
调用即可。element-ui 的 message,vux 的 Toast 都支持插件方式调用。
4. 总结
跟一些成熟的 Vue ui 库,例如:mint-ui 的 Toast 组件相比,现在的 Toast 组件不足之处很多。但通过编写一个极其简单的 Toast 组件,了解到 Vue 组件的部分概念,知道了到如何手动创建,销毁组件,如何结合 transition 制作一些简单的动画,有助于我们更好的理解 Vue 组件的思想 ,而且以后在使用第三方库的一些组件时,能大致知晓其原理。