个人博客地址:https://shizuka.icu,欢迎访问。
前言:出于安全考虑,minio的桶我基本都是设置成了私有的,所以文件都是以流的形式返回的,而我的博客图片都很大,于是就想到了缓存实现,一开始用的Map去缓存,这样来回切换页面就不会去重新请求图片了,缺点是刷新就丢失,后来考虑localstorage,不过好像不支持blob存储,最终决定使用indexeddb去实现。
1. 列表图片懒加载的实现
- IntersectionObserver:具体使用方法,可以简单理解成可以监听一个元素是否在可视化窗口内,然后做某些事——个人理解。
// 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;