背景
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查看内存使用情况

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/bigdata/281740.html