如何写一个函数式组件(vue2,vue3版)
最近迷上了函数式组件,主要是最近接到一个需求,就是公司的产品原来的loading效果都是用uniapp提供的方法,所以这次希望全部统一一种loading效果。不过这次就不带uniapp框架的限制,只考虑vue2、vue3怎么实现一个函数式组件。
准备
这次实现的组件是loading组件,实现的功能主要下面:
// 1、能直接调用
Loading()
// 2、能直接传入文字调用
Loading('加载中...')
// 3、有相关配置调用
Loading({
text: '加载中...',
mask: true
})
// 4、关闭实现
Loading.close()
为了方便使用,可以直接挂载在Vue的实例里面,但是Vue2,Vue3的方式不太相同,实现也不太相同,所以下面会分开实现。
vue2
首先是展示vue文件,这里不做过多的解释,这种程度难不了大家。
<template>
<div class="loading-container" :class="{ pe: !mask }" v-if="visible">
<div class="loading-wrapper" :class="{ mask: mask, pe: !mask }">
<img
src="https://zhanchi-static.oss-cn-shenzhen.aliyuncs.com/zhancchi_recruit/loading.gif"
/>
<div class="loading-text">{{ text }}</div>
</div>
</div>
</template>
<script>
export default {
name: "globalLoading",
data() {
return {
text: "加载中",
visible: false,
mask: false
};
},
methods: {
close() {
this.visible = false;
}
},
watch: {
visible(newVal) {
this.visible = newVal;
}
}
};
</script>
<style lang="scss" scoped>
.pe {
pointer-events: none;
}
.loading-container {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 999;
.loading-wrapper {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
width: 32px;
height: 32px;
}
.loading-text {
display: inline-block;
max-width: 100px;
margin-top: 5px;
font-size: 12px;
color: #666
}
}
.mask {
background: rgba(0, 0, 0, 0.5);
.loading-text {
color: #fff;
}
}
}
</style>
大家可以根据自己的需求设计,下来就是怎么改成函数式组件。
首先,我们需要Vue2里面给我提供的一个方法——extend。给予我们可以构建“子类”的办法,接着创建index.js文件。
import Vue from 'vue';
import Main from './index.vue'
let LoadingConstructor = Vue.extend(Main)
// 保存Loading构造实例
let instance
接着我们需要Loading方法,实现如下:
const Loading = function(options) {
options = options || {}
// 实现功能点2
if(typeof options === 'string') {
options = {
text: options
}
}
// 合并options跟data的属性
instance = new LoadingConstructor({
data: options
})
// 挂载在VNode上面
instance.$mount()
// 插入到页面
document.body.appendChild(instance.$el)
}
在上面将options给data赋值时,会将属性逐一合并,由于一开始我们设置了初始值,所以没有传options的时候,会默认使用组件设置的初始值。那么实现关闭loading的方法就简单了:
Loading.close = function() {
instance && (instance.visible = false)
}
export default Loading
接着只需要在Vue实例挂载即可
// 导入的是index.js的Loading
Vue.prototype.$Loading = Loading
vue3
其实vue3的实现大同小异,只不过由于vue3写法等方面不一样了,所以实现方式也会有不一样。先看template文件:
<script setup>
import { ref } from 'vue'
defineProps({
text: {
type: String,
default: '加载中...',
},
mask: {
type: Boolean,
default: false,
},
})
const visible = ref(false)
function open() {
visible.value = true
}
function close() {
visible.value = false
}
defineExpose({
close,
open,
})
</script>
<template>
<div v-show="visible" class="loading-container" :class="{ pe: !mask }">
<div class="loading-wrapper" :class="{ mask: mask, pe: !mask }">
<img
src="https://zhanchi-static.oss-cn-shenzhen.aliyuncs.com/zhancchi_recruit/loading.gif"
alt=""
/>
<span class="loading-text">{{ text }}</span>
</div>
</div>
</template>
<style lang="scss" scoped>
.loading-container {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
.loading-wrapper {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
width: 32px;
height: 32px;
}
.loading-text {
display: inline-block;
max-width: 100px;
margin-top: 5px;
font-size: 12px;
color: #666;
}
}
.mask {
background: rgb(0 0 0 / 50%);
.loading-text {
color: #fff;
}
}
}
.pe {
pointer-events: none;
}
</style>
在vue2中,我们可以将参数定义在data,通过构造器合并属性的方式来传递参数,但是在vue3中已经不提供这样的方法了,所以需要将参数定义为props
,并且通过defineExpose
暴露方法在外部使用(其实直接修改visible值也可以,但是使用者来说,还是希望更语义化点吧)。
接着是index.js文件
import { createVNode, render } from 'vue'
import Main from './index.vue'
let instance
const container = document.createElement('div')
const Loading = options => {
if (instance) {
document.body.removeChild(container.firstElementChild)
}
let props = options || {}
if (typeof options === 'string') {
props = {
text: options,
}
}
// 创建vnode
instance = createVNode(Main, props)
// 渲染成到容器
render(instance, container)
// container.firstElementChild:实际上我们只挂在了Loading组件
document.body.appendChild(container.firstElementChild)
// 展示loading
const vm = instance.component
// 调用展示方法
vm.exposed.open()
}
Loading.close = () => {
if (instance) {
// 调用关闭方法
instance.component.exposed.close()
}
}
export default Loading
不同于vue2挂载在实例的方法,vue3是下面方式挂载,但调用就是少写个this罢了(因为vue3没有this概念)
import { createApp } from 'vue'
import App from './App.vue'
import Loading from '@/component/loading/index.js'
const app = createApp(App)
app.config.globalProperties.$Loading = Loading
总结
至此就完成了函数式组件的写法,当然这是Loading组件只展示的了最简单实现,比如你可以添加一些回调函数,如:success,faild等等,套路都一样,实现起来也没啥难度,文章到此为止。
转载自:https://juejin.cn/post/7254027262885642299