indexedDB数据库---聊天项目+获取历史聊天记录

文章讲述了在实施chatgpt对话项目时,由于localStorage存储限制,转向使用indexedDB来保存聊天记录。通过indexedDB的仓库和键值对特性,实现了聊天记录的添加、查询和分页加载。在处理中,特别注意了游标查询的顺序调整以保证对话的正确显示。此外,还提供了indexedDB操作的封装代码示例。
摘要由CSDN通过智能技术生成

前言

在做一个chatgpt对话项目的时候,使用了localStoage保存聊天记录,但是聊天记录如果不进行清理,只会越来越多,localStoage有存储限制,所以选用indexedDB.

特点

  • 非关系新数据库 没有表的概念,只有仓库(store)
    数据以键值对存在
  • 持久化存储
    即使删除浏览器缓存,也不会被删除
  • 异步
  • 支持事务
  • 同源策略
    网页只能访问自身域名下的数据库
  • 存储容量大

最终实现

  • state中定义msgList,用来存储聊天记录,v-for遍历的也是msgList

  • 初始只获取30条渲染

  • 发送时先push到state的msgList中,页面同步更新,然后再插入数据库中

  • 接收到消息也时先插入msgList,使页面更新,再插入数据库

  • 下拉到顶部,从数据库获取历史聊天记录

  • 从数据库中从后往前跳过msgList.length条数据获取30条,unshift插入msgList

遇到的问题

获取的聊天记录是最开始插入的记录开始的,需要在使用游标查询时,改变游标的遍历顺序:从前往后改为从后往前, 获取到数据之后还要reverse, 使对话顺序正确.

调用例子(助记)

		//发送消息
		SAVEMSG(state, data) {
			console.log('#插入的是', data);
			state.msgList2.push(data);//仓库中先插入,页面同步更新
			openDB(["msg"], "1").then((db) => {
				console.log("准备插入数据", db);
				addData(db, "AI-msg", data);//数据库中插入,异步,没什么大问题不会失败
			}).catch((err) => {
				console.log('#数据库 写入失败->', err);
			});
		},
		
		// 	获取历史聊天记录
		REFRESHMSG(state, val) {
			openDB(["msg"], "1").then((db) => {
			//打开数据库成功
				console.log("准备查询", db);
				cursorGetDataAndPage(db, 'AI-msg', val.page, 10,state.msgList2.length).then((res) => {
				//游标查询并且跳过已有的记录
					console.log('#查询成功', res);
					//插入头部
					state.msgList2.unshift(...res.reverse()) ;
				});
			}).catch((err) => {
				console.log('#数据库 查询失败->', err);
			});
		},

indexedDB 封装源码

src/indexedDB/index.js 在需要用到的地方,引入所需方法调用即可

/**
 * 打开数据库
 * @param {object} dbName 数据库的名字
 * @param {string} storeName 仓库名称
 * @param {string} version 数据库的版本
 * @param {string} priKey 主键
 * @param {Object} index 索引
 * @return {object} 该函数会返回一个数据库实例
 */
export const openDB = function openDB(dbName, version = 1, storeName, priKey, index) {
	return new Promise((resolve, reject) => {
		//  兼容浏览器
		let indexedDB =
			window.indexedDB ||
			window.mozIndexedDB ||
			window.webkitIndexedDB ||
			window.msIndexedDB;
		let db;
		// 打开数据库,若没有则会创建
		const request = indexedDB.open(dbName, version);
		// 数据库打开成功回调
		request.onsuccess = function (event) {
			db = event.target.result; // 数据库对象
			// console.log("数据库打开成功");
			resolve(db);
		};
		// 数据库打开失败的回调
		request.onerror = function (event) {
			console.log("数据库打开报错");
		};
		// 数据库有更新时候的回调
		if (storeName !== undefined) {
			request.onupgradeneeded = function (event) {
				// 数据库创建或升级的时候会触发
				console.log("创建成功!");
				db = event.target.result; // 数据库对象
				let objectStore;
				// 创建存储库
				objectStore = db.createObjectStore(storeName, {
					keyPath: priKey, // 这是主键
					autoIncrement: true // 实现自增
				});
				// 创建索引,在后面查询数据的时候可以根据索引查
				// 首先创建主键对应的索引
				objectStore.createIndex(priKey, priKey, {unique: true});
				for (let i = 0; i < index.length; i++) {
					// 然后创建主要索引
					objectStore.createIndex(index[i], index[i], {unique: false});
				}

			};
		}
	});
};

/**
 * 新增数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} data 数据
 */
export const addData = function addData(db, storeName, data) {
	let request = db
		.transaction([storeName], "readwrite") // 事务对象 指定表格名称和操作模式("只读"或"读写")
		.objectStore(storeName) // 仓库对象
		.add(data);

	request.onsuccess = function (event) {
		console.log("数据写入成功");
	};

	request.onerror = function (event) {
		console.log("数据写入失败");
	};
};

/**
 * 通过主键读取数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} key 主键值
 */
export const getDataByKey = function getDataByKey(db, storeName, key) {
	return new Promise((resolve, reject) => {
		let transaction = db.transaction([storeName]); // 事务
		let objectStore = transaction.objectStore(storeName); // 仓库对象
		let request = objectStore.get(key); // 通过主键获取数据

		request.onerror = function (event) {
			console.log("事务失败");
		};

		request.onsuccess = function (event) {
			console.log("主键查询结果: ", request.result);
			resolve(request.result);
		};
	});
};

/**
 * 通过游标读取数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 */
export const cursorGetData = function cursorGetData(db, storeName) {
	let list = [];
	let store = db
		.transaction(storeName, "readwrite") // 事务
		.objectStore(storeName); // 仓库对象
	let request = store.openCursor(); // 指针对象
	// 游标开启成功,逐行读数据
	request.onsuccess = function (e) {
		let cursor = e.target.result;
		if (cursor) {
			// 必须要检查
			list.push(cursor.value);
			cursor.continue(); // 遍历了存储对象中的所有内容
		} else {
			console.log("游标读取的数据:", list);
		}
	};
};
/**
 * 游标从后往前分页查询
 * @param db
 * @param storeName
 * @param page
 * @param pageSize
 * @param skip
 * @returns {Promise<unknown>}
 */
export const cursorGetDataAndPage = function cursorGetData(db, storeName, page, pageSize,skip=null) {
	return new Promise((resolve, reject) => {
		console.log('#skip', skip);
		let list = [];
		let counter = 0; // 计数器
		let advanced = true; // 是否跳过多少条查询
		let store = db
			.transaction(storeName, "readwrite") // 事务
			.objectStore(storeName); // 仓库对象
		let request = store.openCursor(null,"prev"); // 指针对象
		request.onsuccess = function (e) {
			let cursor = e.target.result;
			if (page > 1 && advanced) {
				advanced = false;
				cursor.advance(skip||(page - 1) * pageSize); // 跳过多少条
				return;
			}
			if (cursor) {
				// 必须要检查
				list.push(cursor.value);
				counter++;
				if (counter < pageSize) {
					cursor.continue(); // 遍历了存储对象中的所有内容
				} else {
					cursor = null;
					resolve(list);
					console.log("分页查询结果", list);
				}
			} else {
				resolve(list);
				console.log("分页查询结果", list);
			}

		};

		request.onerror = function (e) {
			reject('#数据库查询失败');
		};
	});
};

/**
 * 通过索引和游标查询记录
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} indexName 索引名称
 * @param {string} indexValue 索引值
 */
export const cursorGetDataByIndex = function cursorGetDataByIndex(db, storeName, indexName, indexValue) {
	let list = [];
	let store = db.transaction(storeName, "readwrite").objectStore(storeName); // 仓库对象
	let request = store
		.index(indexName) // 索引对象
		.openCursor(IDBKeyRange.only(indexValue)); // 指针对象
	request.onsuccess = function (e) {
		let cursor = e.target.result;
		if (cursor) {
			// 必须要检查
			list.push(cursor.value);
			cursor.continue(); // 遍历了存储对象中的所有内容
		} else {
			console.log("游标索引查询结果:", list);
		}
	};
	request.onerror = function (e) {
	};
};

/**
 * 通过索引和游标分页查询记录
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} indexName 索引名称
 * @param {string} indexValue 索引值
 * @param {number} page 页码
 * @param {number} pageSize 查询条数
 */
export const cursorGetDataByIndexAndPagea = function cursorGetDataByIndexAndPage(
	db,
	storeName,
	indexName,
	indexValue,
	page,
	pageSize
) {
	let list = [];
	let counter = 0; // 计数器
	let advanced = true; // 是否跳过多少条查询
	let store = db.transaction(storeName, "readwrite").objectStore(storeName); // 仓库对象
	let request = store
		.index(indexName) // 索引对象
		.openCursor(IDBKeyRange.only(indexValue)); // 指针对象
	request.onsuccess = function (e) {
		let cursor = e.target.result;
		if (page > 1 && advanced) {
			advanced = false;
			cursor.advance((page - 1) * pageSize); // 跳过多少条
			return;
		}
		if (cursor) {
			// 必须要检查
			list.push(cursor.value);
			counter++;
			if (counter < pageSize) {
				cursor.continue(); // 遍历了存储对象中的所有内容
			} else {
				cursor = null;
				console.log("分页查询结果", list);
			}
		} else {
			console.log("分页查询结果", list);
		}
	};
	request.onerror = function (e) {
	};
};

/**
 * 更新数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {object} data 数据
 */
export const updateDB = function updateDB(db, storeName, data) {
	let request = db
		.transaction([storeName], "readwrite") // 事务对象
		.objectStore(storeName) // 仓库对象
		.put(data);

	request.onsuccess = function () {
		console.log("数据更新成功");
	};

	request.onerror = function () {
		console.log("数据更新失败");
	};
};

/**
 * 通过主键删除数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {object} id 主键值
 */
export const deleteDB = function deleteDB(db, storeName, id) {
	let request = db
		.transaction([storeName], "readwrite")
		.objectStore(storeName)
		.delete(id);

	request.onsuccess = function () {
		console.log("数据删除成功");
	};

	request.onerror = function () {
		console.log("数据删除失败");
	};
};

/**
 * 通过索引和游标删除指定的数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} indexName 索引名
 * @param {object} indexValue 索引值
 */
export const cursorDelete = function cursorDelete(db, storeName, indexName, indexValue) {
	let store = db.transaction(storeName, "readwrite").objectStore(storeName);
	let request = store
		.index(indexName) // 索引对象
		.openCursor(IDBKeyRange.only(indexValue)); // 指针对象
	request.onsuccess = function (e) {
		let cursor = e.target.result;
		let deleteRequest;
		if (cursor) {
			deleteRequest = cursor.delete(); // 请求删除当前项
			deleteRequest.onerror = function () {
				console.log("游标删除该记录失败");
			};
			deleteRequest.onsuccess = function () {
				console.log("游标删除该记录成功");
			};
			cursor.continue();
		}
	};
	request.onerror = function (e) {
	};
};

/**
 * 关闭数据库
 * @param {object} db 数据库实例
 */
export const closeDB = function closeDB(db) {
	db.close();
	console.log("数据库已关闭");
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值