IndexedDB是一个事务型数据库系统,类似于基于SQL的RDBMS。 然而不同的是它使用固定列表(只有 key,value),IndexedDB是一个基于JavaScript的面向对象的数据库。
IndexedDB允许您存储和检索用键索引的对象; 可以存储structured clone algorithm支持的任何对象。
您只需要指定数据库模式,打开与数据库的连接,然后检索和更新一系列事务中的数据。

虽然indexedDB的使用很方便,存储时间也长,空间也大,但是还是需要注意一些事项

1. 数据类型

IndexedDB 允许存储多种数据类型,包括:

  • 基本类型:例如 stringnumberbooleanDate
  • 对象类型:如 JavaScript 对象(即普通的对象字面量)。
  • 二进制数据:通过 BlobArrayBuffer 存储二进制数据,适合存储文件、图片、音频等。
  • 集合类型:如 ArrayMap 等,但它们会在存储时转换为对象形式。

不过需要注意:

  • 不支持直接存储某些 JavaScript 类型,比如 FunctionundefinedRegExp 等。
  • 不支持循环引用的对象,即包含自引用的对象或深度过大的对象。

2. 键值对存储

IndexedDB 使用 键值对(key-value)存储数据。存储的数据通常包括:

  • 键(Key):用于唯一标识数据项。键可以是整数、字符串、Date 或者由 JavaScript 对象提供的自动生成值。
  • 值(Value):存储的数据对象或基本数据类型。值可以是任何合法的 JavaScript 数据类型。

存储时,每个对象必须有一个唯一的键,并且键值对的存储是通过事务进行的,以确保数据的一致性。

3. 事务(Transaction)

IndexedDB 是基于事务的数据库,每次对数据的操作都会在事务中进行。每个事务包含一组对数据库的读写操作,保证操作的原子性(即所有操作要么都成功,要么都失败)。

  • 事务的隔离性:事务内的数据操作是隔离的,即它不会与其他事务的数据操作产生冲突,直到事务被提交。
  • 事务的可读写:事务可以是只读(readonly)或可读写(readwrite),对于修改数据的操作需要使用可读写事务。

4. 索引(Index)

你可以为对象存储(object store)中的数据创建一个或多个索引,以便根据某个字段或属性快速查找数据。

  • 索引的作用:索引允许你根据某个属性高效地查询数据,而无需遍历整个存储。
  • 索引类型:索引可以是单一属性的(例如根据 name 查询)或复合索引(多个属性组合起来作为查询条件)。

5. 存储空间限制

IndexedDB 的存储空间在不同浏览器和设备中有所不同。通常,存储空间没有明确的硬性限制,而是基于浏览器和设备的存储策略:

  • 浏览器限制:不同浏览器对 IndexedDB 存储大小的限制不同,Chrome、Firefox 和 Safari 等主流浏览器一般会限制在一定大小范围内(如 5MB、50MB 或 100MB 等)。
  • 设备限制:移动设备可能有更严格的存储限制,尤其在存储和下载文件时需要考虑设备的存储空间。

6. 异步操作

IndexedDB 的所有操作都是异步的,包括打开数据库、执行事务、存储数据、查询数据等。它通过事件和回调函数来通知操作的完成状态。常见的回调方法有:

  • onsuccess: 当操作成功时调用。
  • onerror: 当操作失败时调用。

这种设计避免了阻塞浏览器的主线程,尤其在进行大数据存储时非常有用。

7. 版本控制(Versioning)

IndexedDB 支持数据库的版本控制。如果你需要更改数据库的结构(例如添加、删除对象存储或修改索引),必须通过数据库的版本升级进行。

  • 版本升级:在打开数据库时,如果检测到版本号发生变化,onupgradeneeded 事件会被触发,你可以在这个事件中进行数据库结构的更改。
  • 每个数据库都有一个版本号,并且可以对数据库进行结构更新。

8. 支持的数据操作

IndexedDB 支持基本的增删改查操作:

  • 添加(Add):向对象存储中添加数据。
  • 更新(Put):更新现有数据或替换数据。
  • 删除(Delete):删除指定键的数据。
  • 查询(Get):根据键值查询数据。
  • 游标(Cursor):使用游标遍历存储中的所有数据。

9. 错误处理

虽然 IndexedDB 通常运行时不会阻塞,但它会通过回调的 onerror 方法提供错误信息。常见的错误类型包括:

  • ConstraintError:违反了数据库的约束条件,如重复键值。
  • DataError:数据格式不正确。
  • TransactionInactiveError:在事务无效时进行操作。

10. 多线程支持

IndexedDB 的操作是异步的,并且可以在浏览器的多个线程之间共享数据。例如,Web Workers 可以使用 IndexedDB 存储和读取数据,从而避免阻塞主线程。

11. 兼容性

大多数现代浏览器都支持 IndexedDB(如 Chrome、Firefox、Edge、Safari 等)。不过,某些老版本的浏览器可能不完全支持或存在已知的 bug,导致部分功能不可用。你可以通过 indexedDB 对象检查浏览器是否支持 IndexedDB。

我们在这里将indexedDB封装成混入js方便在不同页面使用

1.先新建glbCacheMixin.js

// glbCacheMixin.js
export const glbCacheMixin = {
    methods: {
        // 打开 IndexedDB 数据库
        openDB() {
            return new Promise((resolve, reject) => {
                const request = indexedDB.open('GLBCacheDB', 1);

                request.onupgradeneeded = function (event) {
                    const db = event.target.result;
                    if (!db.objectStoreNames.contains('models')) {
                        db.createObjectStore('models', { keyPath: 'id' }); // 使用 id 作为主键
                    }
                };

                request.onsuccess = function (event) {
                    resolve(event.target.result);
                };

                request.onerror = function (event) {
                    reject('IndexedDB open failed');
                };
            });
        },

        // 缓存文件到 IndexedDB
        cacheGLB(id, Object) {
            this.openDB().then(db => {
                    const transaction = db.transaction('models', 'readwrite');
                    const store = transaction.objectStore('models');
                    store.put({ id, data: Object }); // 存储文件,key 是 id
                
            }).catch(error => console.error('Failed to cache GLB:', error));
        },

        // 从 IndexedDB 中根据 ID 获取缓存的文件
        getGLBById(id) {
            return new Promise((resolve, reject) => {
                this.openDB().then(db => {
                    const transaction = db.transaction('models', 'readonly');
                    const store = transaction.objectStore('models');
                    const request = store.get(id); // 按 ID 获取缓存的文件
                    console.log('request :>> ', request);
                    request.onsuccess = function () {
                        if (request.result) {
                            resolve(request.result.data); // 返回缓存的文件数据
                        } else {
                            resolve(null); // 没有找到缓存
                        }
                    };

                    request.onerror = function () {
                        reject('出现错误!');
                    };
                });
            });
        },
        // 检查是否已经缓存文件
        isGLBCached(id) {
            return this.getGLBById(id).then(data => data !== null);
        }
    }
};

那么我们在页面中使用

//导入
import { glbCacheMixin } from '@/mixins/glbCacheMixin'; // 导入混入

//注册
 mixins: [glbCacheMixin],//此级和data并列


//这个方法是用来检测需要缓存的文件地址是否有效
 checkResource(url) {
            return fetch(url, { method: 'HEAD' })  // 使用 HEAD 请求避免下载整个文件
                .then(response => {
                    if (response.status === 404) {
                        console.log(`资源未找到: ${url}`);
                        return false; // 资源不存在
                    }
                    return true; // 资源存在
                })
                .catch(error => {
                    console.error('检查资源失败:', error);
                    return false; // 请求出错或其他问题
                });
        }

//使用

            this.checkResource(url)
                .then(isValid => {
                    if (isValid) {
                        console.log('资源存在,继续加载');
                        this.checkAndLoadGLB(url, this.mdoel.id)
                    } else {
                        console.log('资源不存在');
                        this.$message.warning('资源不存在,请去相关模块维护!');
                    }
                });

checkAndLoadGLB(url, modelId) {
            let self = this
            this.isGLBCached(modelId).then(cached => {
                if (cached) {
               
                    this.getGLBById(modelId).then(data => {
                        console.log('加载进来了 :>> ');
                        // 这里可以处理已缓存的 GLB 数据,例如加载模型
                        
                    });
                } else {
                    console.log('新文件', url, modelId);
                    
                        self.cacheGLB(modelId, ‘需要存的对象’); // 缓存新文件
                   
                }
            });
        },

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐