Vue3 高阶函数组件

1,124 阅读2分钟
自定义高阶函数组件,实现组件或插槽内容的加载状态,错误,及结果的自动显示,此版本由原来的Vue2改写而来,因为工作实在太忙,没太多时间写具体思路,只给大家参考吧,有不清楚的可以评论留言,我给大家解释,具体用法如下:

hoc.js

/*自定义高阶函数组件,实现组件或插槽内容的加载状态,错误,及结果的自动显示,用法如下:1、数据获取请求定义:_request为返回的Promise对象const _request = (params) => {    return api.get(`api/v3/trade/market`, {showError:false, showLoading:false})}2、组装高阶函数,只有1个参数时,传数据请求函数,2个参数时,位置1为定义的vue组件let hoc = new Hoc(_request)或let hoc = new Hoc(About, _request)3、将组装的高阶函数定义为组件components: {    hoc,}4、组件传参:fixed 为加载状态及错误结果是否全屏显示4.1、无组件参数时,暴露默认数据插槽  #default="{data}" 获取数据<hoc :params="params" #default="{data}" fixed>...</hoc>@:params 为默认插槽时,为请求函数_request传参,展示的内容为声明的高阶函数组件内部插槽内容4.2、有组件传参时,例如组件为About,展示的内容为组件内容,需在About内用如下形式传参和接收数据,组件内有插槽时,可在hoc内设置插槽内容<hoc fixed>...</hoc>About组件内部:export default {  props: ['data'],  setup(){    const params = ref('zh-CN')    return {      params    }  },}*/import FLoading from '@/components/FLoading'import ErrorMC from './hoc_error'import ViewState from './view_state.js'import {SUCCESS} from '@/api'import { getCurrentInstance, onMounted, ref, watch, h } from 'vue'import { useI18n } from 'vue-i18n'export default function(WrappedComponent, promiseFn) {  if(!promiseFn){    promiseFn = WrappedComponent    WrappedComponent = null  }  return {    props: {      params: {        default: null      },      fixed: {        type: Boolean,        default: false      }    },    setup(props, ctx){      const self = getCurrentInstance()      const {t} = useI18n()      const viewState = ref(ViewState.busy)      const message = ref('')      const data = ref(null)      const reqParams = ref(null)      async function request() {        viewState.value = ViewState.busy        const result = await promiseFn(reqParams.value)        if (result.rst != SUCCESS) {          message.value = `${result.code||''} ${t('error_code.'+result.msg).replace('error_code.','')}`          if (result.path) {            message.value = `path: ${result.path}<br/>${message.value}`          }          viewState.value = ViewState.error        } else {          data.value = result.data          viewState.value = ViewState.idle        }      }      onMounted(()=>{        // 立刻发送请求,并且监听参数变化重新请求        reqParams.value = WrappedComponent && self.refs.wrapped.params || props.params        watch(reqParams, ()=>request(), {          immediate: true,          deep:true        })      })      return {        viewState,        message,        data,        request      }    },    render() {      const slots = {...this.$slots}      slots.default = ()=> this.$slots.default({data:this.data})      const args = {        ref: 'wrapped',        data: this.data,        ...this.$attrs,      }      const wrapped = WrappedComponent || "div"      const wrapper = h('div', {        style: this.viewState != ViewState.idle?'position:relative; min-height: 2.1rem;':''      }, [        this.viewState == ViewState.busy ? h(FLoading, {          color: '#c9c9c9',          size: 30,          fixed: this.fixed,          bgColor: 'rgba(0, 0, 0, 0)',        }) : (this.viewState == ViewState.error ? h(ErrorMC, {          message: this.message,          fixed: this.fixed,          onRetry:()=> this.request()        }) : null),        this.viewState != ViewState.idle?        h('div', {style:'visibility: hidden;'}, h(wrapped, args, null)):        h(wrapped, args, slots),      ])      return wrapper    },  }}

view_state.js

export default class ViewState {  static idle = 0  static busy = 1  static empty = 2  static error = 3  static unAuthorized = 4}

hoc_error.vue

<template>  <div class="error-mask" :class="{fixed:fixed}">    <section>      <div class="content">        <p v-html="message"></p>        <div class="pt10">        <van-button type="danger" size="small" block @click="retry">{{$t(`重试`)}}</van-button>      </div>      </div>    </section>      </div></template><script>export default {  props: {    message:String,    fixed:{      type:Boolean,      default:false    }  },  setup(props, ctx){    function retry(){      ctx.emit('retry')    }    return {      retry    }  }}</script><style lang="less" scoped>.error-mask{  position: absolute;  left: 0;  top: 0;  right:0;  bottom:0;  background-color:rgba(0,0,0,.32);  z-index:1;  &.fixed {    position: fixed;  }  section {    margin: 0.2rem;    height: calc(100% - 0.4rem;);    overflow-y: auto;    line-height: 1.5;    color: #FFF;    font-size: 12px;    display: flex;    flex-direction: column;    justify-content: center;    p {word-break: break-all;}  }}</style>