vue3封装图片懒加载

Vue3.0 + Typescript 组件封装


基于vue3.0封装图片加载组件,支持按需加载功能。

import { ref, defineComponent, PropType, ComponentPublicInstance, VNode } from 'vue'
// 图片加载状态枚举值
export enum Status {
  LOADING = 0,
  SUCCESS = 1,
  FAIL = -1
}
// 定义组件 Props 属性接口
export interface OitImageProps {
  src: string
  width: string
  height: string
  fit: 'contain' | 'cover' | 'none' | 'scale-down' | 'initial' | 'inherit'
  props: {
    lazy?: boolean
    lazyLoad?: (resolve: () => unknown) => void
  }
}
const components = {
  success: (props: OitImageProps) => (
    <div class='oit__image--success'>
      <img src={props.src} style={{ objectFit: props.fit }} />
    </div>
  ),
  fail: () => (
    <div class={styled['oit__image--error']}>
      <img src='https://via.placeholder.com/150x100.png?text=Fail' />
    </div>
  ),
  loading: () => (
    <div class='oit__image--loading'>
      <div class={styled['oit__image--error']}>
        <img src='https://via.placeholder.com/150x100.png?text=Loadding' />
      </div>
    </div>
  )
}

const OitImage = defineComponent<OitImageProps>(props => {
  const { props: lazyProps } = props
  const code = ref<Status>(Status.LOADING)
  const wrapper = () => {
    const image = new Image()
    image.onload = function() {
      code.value = Status.SUCCESS
    }
    image.onerror = function() {
      code.value = Status.FAIL
    }
    image.src = props.src
  }
  if (lazyProps?.lazy && lazyProps?.lazyLoad) {
    lazyProps.lazyLoad(wrapper)
  } else {
    wrapper()
  }

  return (vm: ComponentPublicInstance): JSX.Element => {
    let c: VNode[] | JSX.Element = components.loading()
    if (code.value === Status.SUCCESS) {
      c = components.success(props)
    } else if (code.value === Status.FAIL) {
      if (vm.$slots.error) {
        // 使用用户自定义的错误模版
        c = vm.$slots.error()
      } else {
        c = components.fail()
      }
    }
    return (
      <div class={styled.oit__image} style={{ width: props.width, height: props.height }}>
        <div class={styled['oit__image-inner']}>{c}</div>
      </div>
    )
  }
})

OitImage.props = {
  src: {
    type: String as PropType<OitImageProps['src']>,
    require: true
  },
  width: {
    type: String as PropType<OitImageProps['width']>,
    default: '150px'
  },
  height: {
    type: String as PropType<OitImageProps['height']>,
    default: '100px'
  },
  fit: {
    type: String as PropType<OitImageProps['fit']>,
    default: 'initial'
  },
  props: {
    type: Object as PropType<OitImageProps['props']>,
    validator: (props: OitImageProps['props']) => {
      if (props.lazy && !(props.lazyLoad || typeof props.lazyLoad === 'function')) {
        console.warn('lazy needs to be used with lazyLoad.')
        return false
      }
      return true
    }
  }
}

export default OitImage

下面是使用方式:

import { defineComponent } from 'vue'
import Image, { OitImageProps } from '@/components/Image'
export default defineComponent(() => {
  const lazyProps:OitImageProps['props'] = {
    lazy: true,
    lazyLoad: (resolve) => {
      setTimeout(() => resolve(), 3000)
    }
  }

  return (): JSX.Element => {
    return (
      <div>
        <p>欢迎你学习Vue.js</p>
        <Image props={lazyProps} src={require('@/assets/images/cover.jpeg')} width="150px" height="100px"></Image>
      </div>
    )
  }
})

作者第一次发表文章!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>