浏览器数据库IndexedDB和前端多线程webWorker在3D场景中的实战应用
作者:互联网
背景
1.IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。
2.在3D场景中模型数据很大,有可能存在数十万级的数据存储,大量数据存储在内存中会很容易导致内存溢出,因此采用indexedDB存储大量数据,减少占用浏览器内存引发的页面卡顿或者页面崩溃等性能问题
3.现有的浏览器数据储存方案都不适合储存大量数据:Cookie 的大小不超过4KB;LocalStorage 在 2.5MB 到 10MB 之间;IndexedDB的储存量视磁盘大小而定,具体说明如下:
4.3D点云的文件格式.pcd,threejs加载3d点云的需要使用PCDLoader加载器,在PCDLoader.js源码中加载pcd文件需要遍及数十万级的数据并计算position,points的信息,这个过程耗时较长,会导致js阻塞从而页面会出现较长时间的卡顿,采用web worker增加多线程把计算position,points的信息这一步放在worker中执行,减少页面卡顿时间
前端代码
1.indexedDB数据库
第一步:创建数据库
1 var indexedDB = window.indexedDB; 2 if (!indexedDB) { 3 console.log('你的浏览器不支持IndexedDB'); 4 } else { 5 var request = indexedDB.open("pcdpointsDB", 1); 6 console.log('数据库创建.....'); 7 let db = null; 8 request.onupgradeneeded = event => { 9 db = (event as any).target.result; 10 //建表 名为person,主键为id 11 let store = db.createObjectStore('pcdInfo', { 12 keyPath: 'index' 13 }); 14 store.createIndex('position', 'position', { 15 unique: false 16 }); 17 store.createIndex('pcdPoints', 'pcdPoints', { 18 unique: false 19 }); 20 store.createIndex('index', 'index', { 21 unique: false 22 }); 23 store.createIndex('mesh', 'mesh', { 24 unique: false 25 }); 26 } 27 request.onsuccess = event => { 28 db = (event as any).target.result; 29 console.log('数据库创建成功') 30 db.transaction(['pcdInfo'], 'readwrite').objectStore('pcdInfo').add({ 31 index: index, 32 position: position, 33 pcdPoints: pcdPoints, 34 mesh:mesh.toJSON() 35 }) 36 console.log('数据库新增数据......') 37 db.close()//关闭数据库 38 } 39 request.onerror = function (event) { 40 console.log('打开数据库失败'); 41 } 42 }
第二步:获取数据库中的数据
1 getIndexedDB(index) { 2 return new Promise((resolve, reject) => { 3 let request = indexedDB.open('pcdpointsDB'); 4 request.onsuccess = event => { 5 console.log('数据库获取----open成功'); 6 let db = (event as any).target.result; 7 var transaction = db.transaction(['pcdInfo']); 8 let objectStore = transaction.objectStore('pcdInfo'); 9 let res = objectStore.get(index); 10 res.onsuccess = function (event) { 11 if (res.result) { 12 console.log(`${'数据库获取成功' + (index + 1)}`); 13 resolve(res.result) 14 } else { 15 console.log(`${'数据库获取---未获得数据记录' + (index + 1)}`); 16 resolve(null) 17 } 18 db.close()//关闭数据库 19 20 21 }; 22 res.onerror = function (err) { 23 reject(err) 24 } 25 26 } 27 request.onerror = function (err) { 28 reject(err) 29 } 30 }) 31 }
通过async/await获取具体值
1 async loadPcdData() { 2 let result: any = await this.getIndexedDB(this.fileIndex); 3 let { pcdPoints = [],mesh } = result; 4 let loader = new THREE.ObjectLoader(); 5 let parseMesh = loader.parse(mesh,()=>{}); 6 }
第三步:删除数据库中的数据
1 let request = indexedDB.open('pcdpointsDB'); 2 request.onsuccess = event => { 3 console.log('数据库删除----open成功'); 4 let db = (event as any).target.result; 5 var transaction = db.transaction(['pcdInfo'], 'readwrite'); 6 let objectStore = transaction.objectStore('pcdInfo'); 7 let res = objectStore.delete(index); 8 res.onsuccess = function (event) { 9 console.log(`${'数据库删除成功' + (index + 1)}`); 10 db.close()//关闭数据库 11 };
2.web worker多线程
- web workers可以把耗时的计算放在非主线程里面。从而充分发挥电脑的性能;
- 创建一个webworker子线程只需要一个脚本js文件url和new Worker(url),这个脚本文件就在新创建的子线程里加载和运行
1 let worker=new Worker(workerUrl+'/worker.js') //url是脚本文.件地址 2 // 发送消息 3 worker.postMessage(res); 4 worker.onmessage = function(event){ 5 let { pcdPoints,position,color,normal } =event.data; 6 worker.terminate() //关闭主进程 7 }
- 子进程中的执行环境跟主线程环境是隔离的,子进程的全局环境是self或者this,处理完的数据通过self.postMessage传递给主线程,主线程通过worker.onmessage接收。
1 onmessage = function (event) { 2 let { pointsPCD, dataview,url } = event.data; 3 let pcdResult = setPcdloaderResult(pointsPCD, dataview); 4 postMessage(pcdResult); 5 this.close() //关闭线程 6 }
- worker.postMessage(参数)参数就是传递给子线程需要处理的数据,
注意:1.函数方法或者类无法传递
- Sources下的Page可以看到worker.js脚本文件,释放worker子线程就会消失;worker子线程会耗费大量的资源,而且不会自动的被释放,通过上述代码创建完之后,如果不需要了,我们需要手动释放。 释放worker子线程有两种方法。
- 子线程及时关闭 this.close()或者self.close();
- 主线程关闭worker.terminate()
开发过程中的问题验证
1.场景使用:worker.js有可能需要引入模块化的包,比如import或者require,目前导入模块包会导致脚本文件不执行,无法查看报错原因
技术方案:采用worker-loader插件,webpack配置
npm I –D worker-loader
1 { 2 test: /\.testWorker\.js$/, 3 use: { loader: "worker-loader" }, 4 },
入口文件配置
1 entry: { 2 worker: './src/utils/worker.js', 3 },
2.3d点云中的mesh模型对象存储到indexedDB时,从indexedDB中获取到对象之后怎么反解析成Mesh对象
技术方案实现:
- mesh继承自Object3D,其中有一个方法,toJSON()可以以json格式返回对象的数据;
- 将mesh对象.toJSON()存储到indexedDB,拿到jsonmesh使用ObjectLoader解析出来
-
1 let jsonmesh=mesh.toJSON(); 2 let loader = new THREE.ObjectLoader(); 3 let parseMesh = loader.parse(mesh,()=>{}); 4 console.log(parseMesh)
其他导致内存溢出的情况
- 避免console.log打印这些数十万级的position,pcdPoints数组,非常消耗性能
- 全局环境中避免过多存储position,pcdPoints等全局变量,非常消耗性能,可以在indexedDB获取你需要的数据,在函数中作为参数传递,函数执行完毕就会被回收销毁
- 通过memory查看内存使用情况
标签:IndexedDB,indexedDB,console,数据库,worker,webWorker,let,多线程,event 来源: https://www.cnblogs.com/wsqspace/p/16613943.html