微信小程序开发过程中,使用web-view标签引入H5页面,会发现例如glb等三维模型文件,哪怕服务器设置了http缓存,也不会生效,用户每次打开页面还是要重新加载三维模型,而一些三维模型的体积可能又会比较大,所以就考虑将3D模型缓存下来,经过实践尝试后,封装了用于处理此场景的函数工具类,希望能帮助需要做此块改进优化的人。
/**
* 此工具类是对三维模型数据存储到浏览器indexDB的封装
* 【为什么使用indexDB存储三维模型数据】:
* >> 由于微信浏览器glb模型的浏览器缓存不生效(服务器配置了强缓存和协商缓存,但每次还是需要重新拉取),出于用户体验优化的考虑,直接缓存到浏览器数据库中
* 【实践过程】:
* 1. 最开始尝试直接存储加载glb的模型的json数据,无法直接存储
* 2. 其次尝试JSON.stringify转换成字符串,由于此方法原型链丢失,也无法直接使用
* 3. 最终采用blob类型存储实现
* 【如何使用?】参照如下:
* const indexDB = new DBUtils(dbName, storeName, dbVersion)
* indexDB.get(modelUrl).then(blob => {
* const url = URL.createObjectURL(new Blob([blob]));
* const loader = new GLTFLoader();
* loader.load(url, (gltf) => {
* const root = gltf.scene
* ...(下面部分就和threejs加载glb模型代码一样,按业务需求做相应的操作)
* }, xhr => {
* // 此处可以计算加载进度
* const {loaded, total } = xhr
* console.log(`已加载${loaded / total * 100}%`)
* })
*/
export default class DBUtils {
constructor(dbName, storeName, dbVersion = 1) {
this.dbName = dbName;
this.storeName = storeName;
this.dbVersion = dbVersion;
this.db = null;
}
async get(url) {
this.db = await this.initDataBase(this.dbName, this.storeName, this.dbVersion);
const request = this.db
.transaction([this.storeName], 'readwrite')
.objectStore(this.storeName)
.get(url);
return new Promise((resolve, reject) => {
request.onsuccess = evt => {
const modelFile = evt.target.result;
// 如果缓存中有,直接从缓存中获取
if (modelFile) {
resolve(modelFile.blob);
} else {
// 如果缓存中没有,则请求资源
this.addData(url).then(blob => {
resolve(blob);
}).catch(() => {
reject();
});
}
};
request.onerror = evt => {
console.log('error', evt);
reject();
};
});
}
async addData(url) {
// 如果使用vue,也可以用axios请求
const res = await fetch(url, { method: 'get' });
if (res.status == 200) {
let blob = null;
// 采用fetch的blob()并不是针对所有文件下载都成功,比如json文件得用json(),还有一些编码为gbk格式的用blob()也可能失败,所以采用arrayBuffer和blob兼容处理
try {
blob = await res.arrayBuffer();
} catch (e) {
// 采用arrayBuffer方式失败,改用blob方式
blob = await res.blob();
}
const obj = {
url,
blob: new Blob([blob])
};
const request = this.db
.transaction([this.storeName], 'readwrite')
.objectStore(this.storeName)
.add(obj);
return new Promise((resolve, reject) => {
request.onsuccess = () => {
// 添加数据成功
resolve(obj.blob);
};
request.onerror = evt => {
console.log('添加数据失败', evt);
reject();
};
});
}
}
initDataBase(dbName, storeName, dbVersion = 1) {
if (!window.indexedDB) {
console.log('您的浏览器不持支此版的IndexDB');
} else {
const request = indexedDB.open(dbName, dbVersion);
return new Promise((resolve, reject) => {
request.onerror = () => {
console.log('error: 创建db时出现异常');
reject();
};
request.onupgradeneeded = (evt) => {
evt.currentTarget.result.createObjectStore(storeName, {
keyPath: 'url'
});
};
request.onsuccess = (evt) => {
resolve(evt.target.result);
};
});
}
}
}