基于localforage和IntersectionObserver实现minio的图片懒加载以及图片流缓存

个人博客地址:https://shizuka.icu,欢迎访问。

前言:出于安全考虑,minio的桶我基本都是设置成了私有的,所以文件都是以流的形式返回的,而我的博客图片都很大,于是就想到了缓存实现,一开始用的Map去缓存,这样来回切换页面就不会去重新请求图片了,缺点是刷新就丢失,后来考虑localstorage,不过好像不支持blob存储,最终决定使用indexeddb去实现。

1. 列表图片懒加载的实现

  • IntersectionObserver:具体使用方法,可以简单理解成可以监听一个元素是否在可视化窗口内,然后做某些事——个人理解。

Snipaste_2023-10-16_16-29-33.png

// ArtilceList.vue
// 监听元素,当出现在可视区调用useFileStream钩子
const observe = () => {
  const imgList = document.querySelectorAll('.list');

  var io = new IntersectionObserver((entries) => {
    entries.forEach((item) => {
      if (item.isIntersecting) {
        const dom = item.target as HTMLElement;
        const url = dom.getAttribute('origin-url');
        const filename = url?.split('/').at(-1);
        filename && useFileStream(filename, '桶名', dom);
        io.unobserve(item.target);
      }
    });
  });

  imgList.forEach((img) => {
    return io.observe(img);
  });
};

2. 图片流缓存的实现

  • localforage:基于indexeddb封装的库,使用同localstorage,不过它是异步的。具体使用方法
// useFileStream.ts
// 添加过期时间,这样每次刷新只要没过期则不会再发送图片请求,实现缓存
import { getFileStream } from '/@/api/minio';
import localforage from 'localforage';

interface FileObj {
  key: Blob;
  expires: number;
}

const myIndexedDB = localforage.createInstance({
  name: 'myIndexedDB',
});

export const useFileStream = async (
  filename: string,
  bucket = '',
  dom: HTMLElement,
) => {
  try {
    const target = (await myIndexedDB.getItem(filename)) as null | FileObj;
    if (target) {
      const expires = target.expires;
      if (Date.now() > expires) {
        await myIndexedDB.removeItem(filename);
        await getImgUrl(filename, bucket, dom);
      } else {
        setBackground(target, dom);
      }
    } else {
      return await getImgUrl(filename, bucket, dom);
    }
  } catch (err) {
    console.log(err);
  }
};

const getImgUrl = async (filename: string, bucket: string, dom: HTMLElement) => {
  const res = await getFileStream(filename, { bucket });
  const blob = new Blob([res.data], { type: 'image/png' });
  const expires = getExpire();
  const fileObj = {
    expires,
    key: blob,
  };
  myIndexedDB.setItem(filename, fileObj);
  setBackground(fileObj, dom);
};

const setBackground = (target: FileObj, dom: HTMLElement) => {
  const url = window.URL.createObjectURL(target.key);
  dom.style['background'] = `url(${url})`;
  dom.style['background-size'] = 'cover';
  dom.style['background-position'] = '50% 50%';
};

const getExpire = () => Date.now() + 3600 * 1000 * 24 * 7;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值