Vue3 对组件库组件二次封装的使用,以骨架屏为例

记录下我对组件库二次封装的体会,以及部分优化,有误望告知

技术:使用的是wot design uni组件库的 <wd-skeleton>组件

目的:通过骨架屏来增加用户等待体验。

呈现效果:在小程序上有骨架屏作为loading的等待,兼容浏览器的响应式网页和小程序

做二次封装的目的:组件库的组件部分功能不满足需求,譬如骨架屏,发现源码使用v-if,因此不能并行渲染,所以后续有优化。

组件地址:Skeleton 骨架屏 | Wot Design Uni

一阶段:

初始阶段,就是很直白的引入组件,在同个界面下使用 <wd-skeleton>,定义需要的值进行传递。

缺点:可复用性和可拓展性差,重复性高,多个页面维护困难

<template>
    <wd-skeleton
          animation="gradient"
          theme="paragraph"
          :custom-style="showSkeleton ? { padding: '12rpx' } : {}"
          :loading="showSkeleton"
          :row-col="rowcol">
        <view>
            <!--其他内容-->
        </view>
    <wd-skeleton>
</template>
<script lang="ts" setup>
    const showSkeleton = ref(true)

    const rowcol = reactive([
      { width: '100%', height: '110rpx' },
      { width: '74%', height: '48rpx', marginTop: '-12rpx' },
      { width: '42%', height: '54rpx', marginTop: '-12rpx', marginBottom: '18rpx' },
      { width: '40%', height: '64rpx', marginTop: '36rpx', marginBottom: '16rpx' },
      { width: '100%', height: '108rpx' },
      { width: '100%', height: '300rpx' },
    ])

    // 还有在函数里,接口数据请求成功后,将showSkeleton 设置false隐藏
    async function queryProjectInfo(Id) {
      showSkeleton .value = true
      const query = {
        id: Id,
      }
      const { errcode, data } = await getData(query)
      if (errcode === 0) {
        //  其他判断
        //  XXX
      }
      showSkeleton .value = false
    }

</script>

二阶段:

抽离公用的组件Skeleton,在component下定义所对应的skeleton组件,定义样式和数据,传值给公共组件Skeleton,然后在目标页面下引入定义的skeleton组件,相当于两次值传递

优点:可复用性提高了,提升了可维护性和可拓展性

缺点:重复性依旧很高,额外封装的页面结构相似性高

公共组件Skeleton.vue

<route lang="json5" type="component">
{
  desc: '自定义业务骨架屏',
}
</route>

<template>
  <wd-skeleton
    animation="gradient"
    theme="paragraph"
    :custom-style="props.customStyle"
    :loading="props.loading"
    :row-col="props.rowcol"
  ></wd-skeleton>
</template>

<script lang="ts" setup>
import { defineProps } from 'vue'

const props = defineProps({
  customStyle: {
    type: Object as () => Record<string, string | number>,
    default: () => ({}),
  },
  loading: {
    type: Boolean,
    default: true,
  },
  rowcol: {
    type: Array as () => Array<{
      width: string
      height: string
      marginTop?: string
      marginBottom?: string
    }>,
    default: () => [],
  },
})
</script>

<style lang="scss" scoped></style>

定义的组件AnalyzeSkeleton.vue

<template>
  <view style="position: relative">
    <Skeleton
      :custom-style="showContent ? { padding: '12px' } : {}"
      :loading="showSkeleton"
      :rowcol="rowCol"
    ></Skeleton>
  </view>
</template>

<script lang="ts" setup>
import Skeleton from '@/components/skeleton/index.vue'

defineProps({
  showSkeleton: {
    type: Boolean,
    default: true,
  },
})

const rowCol = reactive([
  { width: '100%', height: '110rpx' },
  { width: '74%', height: '48rpx', marginTop: '-12rpx' },
  { width: '42%', height: '54rpx', marginTop: '-12rpx', marginBottom: '18rpx' },
  { width: '40%', height: '64rpx', marginTop: '36rpx', marginBottom: '16rpx' },
  { width: '100%', height: '108rpx' },
  { width: '100%', height: '300rpx' },
])
</script>

<style scoped>

引入该组件

<template>
    <AnalyzeSkeleton :loading="showSkeleton">
        <view>
            <!--其他内容-->
        </view>
    <AnalyzeSkeleton >
</template>

<script setup lang="ts">
    import AnalyzeSkeleton from './components/skeleton.vue'
    const showSkeleton = ref(true)
    // 还有在函数里,接口数据请求成功后,将showSkeleton 设置false隐藏
    async function queryProjectInfo(Id) {
      showSkeleton.value = true
      const query = {
        id: Id,
      }
      const { errcode, data } = await getData(query)
      if (errcode === 0) {
        //其他判断
        XXX
      }
      showSkeleton .value = false
</script >

虽然提高了可复用性,但还未能很好的封装,冗余还是比较多,有点像脱裤子放屁,多此一举

三阶段:

将骨架屏结构定义通过ts文件维护,复用公用的组件Skeleton就行,通过不同的值进行结构切换。

公共组件Skeleton.vue

<route lang="json5" type="component">
    {
      desc: '自定义业务骨架屏',
    }
</route>

<template>
    <wd-skeleton
      animation="gradient"
      theme="paragraph"
      :custom-style="props.customStyle"
      :loading="props.showLoading"
      :row-col="rowColMap[props.rowcol]"
    ></wd-skeleton>
</template>

<script lang="ts" setup>
    import { defineProps } from 'vue'
    import { rowColMap } from './rowCol'

    const props = defineProps({
      customStyle: {
        type: Object as () => Record<string, string | number>,
        default: () => ({}),
      },
      showLoading: {
        type: Boolean,
        default: true,
      },
      rowcol: {
        type: String,
        default: 'PRJ_DETIAL',
      },
    })
</script>

<style lang="scss" scoped>

</style>

ts文件定义骨架屏结构

export const rowColMap = {
      ANALYZE_SKELETON: [
        { width: '100%', height: '110rpx' },
        { width: '74%', height: '48rpx', marginTop: '-12rpx' },
        { width: '42%', height: '54rpx', marginTop: '-12rpx', marginBottom: '18rpx' },
        { width: '40%', height: '64rpx', marginTop: '36rpx', marginBottom: '16rpx' },
        { width: '100%', height: '108rpx' },
        { width: '100%', height: '300rpx' },
      ],
        //  其他的XXX
}

export const rowColKeys = {
      ANALYZE_SKELETON: 'ANALYZE_SKELETON',
      //  其他的XXX
}

引入该组件

<template>
    <AnalyzeSkeleton
      :show-loading="skeletonLoading"
      :custom-style="skeletonLoading ? { padding: '12px' } : {}"
      :rowcol="rowCol"
    >
    <!--其他内容-->
    </<AnalyzeSkeleton>

</template>

<script ts='lang' setup>
    import AnalyzeSkeleton from '@/components/skeleton/skeleton.vue'
    import { rowColKeys } from '@/components/skeleton/rowCol'
    const rowCol = rowColKeys.ANALYZE_SKELETON
    // 改了下命名
    const skeletonLoading = ref(true)
    // 还有在函数里,接口数据请求成功后,将showSkeleton 设置false隐藏
    async function queryProjectInfo(Id) {
      skeletonLoading.value = true
      const query = {
        id: Id,
      }
      const { errcode, data } = await getData(query)
      if (errcode === 0) {
        //其他判断
        XXX
      }
      skeletonLoading.value = false
</script>

优点:抽离出来,复用性和维护性提高,只需要维护ts文件就可以增加修改结构

p.s. 目前只做到这一步,可能还有更好的封装方案(dog.jpg

其他优化:

看了下<wd-skeleton>组件的代码,发现使用的v-if,因此会等骨架屏消失后再渲染,希望在加载骨架屏的同时能并行

<wd-skeleton>组件代码如下:

<template>
  <view :class="`wd-skeleton ${customClass}`" :style="customStyle">
    <view class="wd-skeleton__content" v-if="show">
      <view class="wd-skeleton__row" v-for="(row, index) of parsedRowCols" :key="`row-${index}`">
        <view v-for="(col, idx) of row" :key="`col-${idx}`" :class="col.class" :style="col.style" />
      </view>
    </view>
    <view v-else>
      <slot />
    </view>
  </view>
</template>

设计了三套方案:

1. 不传进<wd-skeleton>的插槽中,额外插槽使用v-show

2. 做个补丁对<wd-skeleton>处理

3. 将未加载的页面做偏移隐藏,后续骨架屏消失后再显示出来

后面考虑后使用了第三种方案,原因:

第一种,使用v-show可行,但是由于部分页面使用了雷达图等,是根据页面高度计算的,使用v-show会显示不出来

第二种,会对该组件产生全局影响,暂时不考虑

所以考虑了偏移

1. 使用absolute进行脱标处理

2. 使用translate进行偏移

3. 使用overflow:hidden,隐藏超出的页面,避免页面滚动

使用到了windicss改变样式

<route lang="json5" type="component">
{
  desc: '自定义业务骨架屏',
}
</route>

<template>
  <view class="bg-#f2f5f7 relative" :class="[props.showLoading && 'max-h-100vh overflow-hidden']">
    <wd-skeleton
      animation="gradient"
      theme="paragraph"
      :custom-style="props.customStyle"
      :loading="props.showLoading"
      :row-col="rowColMap[props.rowcol]"
    ></wd-skeleton>
    <view :class="{ slotClass: props.showLoading }">
      <slot></slot>
    </view>
  </view>
</template>

<script lang="ts" setup>
import { defineProps } from 'vue'
import { rowColMap } from './rowCol'

const props = defineProps({
  customStyle: {
    type: Object as () => Record<string, string | number>,
    default: () => ({}),
  },
  showLoading: {
    type: Boolean,
    default: true,
  },
  rowcol: {
    type: String,
    default: 'ANALYZE_SKELETON',
  },
})
</script>

<style lang="scss" scoped>
.slotClass {
  position: absolute;
  top: 0;
  width: 100%;
  height: auto;
  transform: translateX(100%);
}
</style>

结果():

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值