1

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


陈东民
2.1k 声望269 粉丝

坚持自我 纯粹的技术