scene problem
Recently, when loading a large model, when loading the file, it will parse out how many mesh
, line
, parameter
there are in the file, and then there will be a variable to maintain this relationship:
const detailedList = {
mesh: Array,
line: Array,
parameter: Array
}
This will cause the memory to be tight, after all, several gigabytes of data information are directly stored.
Then there is another way of saying that the file can't be rendered directly after it is loaded, right?
But this will lead to too many DC
, CPU
can't bear it.
IndexDB
Using indexDB
to act as a memory function similar to , I selectively store the data in indexDB, and take it when needed, which will greatly relieve the memory pressure. indexDB is similar to NOSQL, it is not a two-dimensional table structure like MYSQL, and the speed of fetching data is fast.
Construct a Store
Like most front-end projects, I also constructed a store.
enum StoreType {
MEMORY,
INDEXDB
}
class Store {
/**
* 存放在内存中的数据
*/
private memory: Map<string, any>
private readonly dbVersion: number;
/**
* indexDB
*/
private db: IDBDatabase;
private isReady: boolean;
/**
* 储存的value多大才使用indexDB储存,反之使用map
*/
private readonly limitForDB: number;
/**
* 达到储存上限后一次性删除多少条数据
*/
private readonly deleteCount: number;
/**
* 储存上限
*/
private readonly upperStorageLimit: number;
constructor(dbVersion = 1, limitForDB?: number) {
this.isReady = false
this.dbVersion = dbVersion
this.initIndexDB()
this.memory = new Map()
this.limitForDB = limitForDB || 30
this.upperStorageLimit = 10000
this.deleteCount = 20
}
set(key: string, value: any, type?: StoreType) {
const intoMemory = () => {
this.memory.set(key, value)
}
const intoIndexDB = () => {
const objectStore = this.getObjectStore();
objectStore.put({id: key, value}, key);
this.calculationTimesAndDelete();
}
if (typeof type !== 'undefined') {
type === StoreType.INDEXDB ? intoIndexDB() : intoMemory()
} else {
if (Store.sizeOf(value) > this.limitForDB) {
// indexDB加之前删除map中存在的key
this.clear(key, StoreType.MEMORY)
intoIndexDB()
} else {
// 加入map之前删除indexDB中存在的key
this.clear(key, StoreType.INDEXDB)
intoMemory()
}
}
}
get(key: string, type?: StoreType): Promise<any> {
return new Promise((resolve, reject) => {
const fromMemory = (key) => {
resolve(this.memory.get(key))
}
const fromIndexDB = (key) => {
const objectStore = this.getObjectStore();
const index = objectStore.index("id");
const res = index.get(key);
res.onsuccess = (e) => {
// @ts-ignore
resolve(e.target.result.value)
}
res.onerror = (e) => {
reject(e)
}
}
if (typeof type !== 'undefined') {
type === StoreType.INDEXDB ? fromIndexDB(key) : fromMemory(key)
} else {
this.memory.has(key) ? fromMemory(key) : fromIndexDB(key)
}
})
}
/**
* 查询数据库条数
*/
calculationTimesAndDelete() {
const result = this.getObjectStore().count()
result.onsuccess = (e) => {
// @ts-ignore
if (e.result > this.upperStorageLimit) {
this.deleteFromPrevious()
}
}
}
/**
* 达到阈值后删除最开始建立的数据
* @description 防止数据膨胀
*/
deleteFromPrevious() {
const objectStore = this.getObjectStore()
const request = objectStore.openCursor();
let count = 0
request.onsuccess = (event) => {
// @ts-ignore
const cursor = event.target.result
if (cursor) {
if (count === this.deleteCount) return;
count++;
objectStore.delete(cursor.key)
cursor.continue()
}
}
}
getObjectStore() {
const transaction = this.db.transaction(["store"], "readwrite");
return transaction.objectStore("store")
}
clear(key: string, type?: StoreType) {
const fromMemoryDel = () => this.memory.delete(key)
const fromIndexDBDel = () => {
const objectStore = this.getObjectStore();
objectStore.delete(key)
}
if (typeof type !== 'undefined') {
type === StoreType.INDEXDB ? fromIndexDBDel() : fromMemoryDel()
} else {
this.memory.has(key) ? fromMemoryDel() : fromIndexDBDel()
}
}
initIndexDB() {
// @ts-ignore
const indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB
if (!indexedDB) {
alert('indexDB被浏览器禁止!')
throw new Error('indexDB forbidden in the browser!')
}
const request = indexedDB.open("elephantFiles", this.dbVersion)
request.onerror = function () {
throw new Error('Error creating/accessing IndexedDB database');
}
request.onsuccess = event => {
// @ts-ignore
this.db = event.target.result as IDBDatabase;
this.isReady = true
}
request.onupgradeneeded = (event) => {
console.log('database upgradeneeded!')
// @ts-ignore
const db = event.target.result as IDBDatabase;
if (!db.objectStoreNames.contains('store')) {
const store = db.createObjectStore('store')
store.createIndex('id', 'id')
}
}
}
/**
* @description 不确认是否初始化完毕时 将查询赋值等操作包裹在此
*/
ready(callback: Function) {
const step = () => {
if (this.isReady) {
callback && callback()
return;
}
window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
}
/**
* @description 估算字节大小, 忽略key的长度, 如果是object只计算value, 比JSON.stringify更快
*/
static sizeOf(value): number {
const typeSizes = {
"undefined": () => 0,
"boolean": () => 4,
"number": () => 8,
"string": item => 2 * item.length,
"object": item => !item ? 0 : Object
.keys(item)
.reduce((total, key) => size(key) + size(item[key]) + total, 0)
};
const size = value => typeSizes[typeof value](value);
return size(value)
}
}
export {StoreType, Store}
use
const a = new Store();
a.ready(() => {
a.set('a', { k: 'dsdsdsdsdssdsd', p: [123, 'sdsdsdsdsdsdsdvvvvvvvvv'] })
a.set('b', 123)
a.get('b')
a.get('a')
})
Adjust limitForDB
and upperStorageLimit
appropriately according to the client's client conditions
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。