web 存储
前端有很多地方可以存放数据.
cookie
对于没有 HttpOnly 的 cookie, 可以通过 document.cookie 来访问. 同时可以使用 js 来创建 cookie, js 创建的 cookie 不能是 HttpOnly 的.
不过应该没人会在 cookie 这里存东西吧.
简单存储 storage
本地存储, 可以保存键值对, 要求都是字符串.
常用的应该只有这四个. key 和 value 都是字符串.
Storage.setItem(key, value)
Storage.getItem(key)
Storage.removeItem(key)
Storage.clear()
localStorage 和 sessionStorage 的用法差不多, 基本上都这样.
还有一个 Storage 事件, 当 Storage 发生改变时, 比如创建, 更新, 删除, 就会触发. 如果设置的是相同的键值对不会触发.
window.addEventListener('storage', (e) => {
console.log(e.key)
console.log(e.oldValue)
console.log(e.newValue)
console.log(e.url)
console.log(e.storageArea)
})
clear()
只会触发一次事件.
localStorage
localStorage 根据环境, 一般是长期有效. 微信浏览器貌似对这个有限制, 不清楚多少天就删除.
同一个域的 localStorage 是共享的, 也就是说, https://example.com/aaa可以访问在https://example.com/设置的localStorage.
sessionStorage
跟 localStorage 的区别只在于, sessionStorage 只在会话期间有效, 也就是说页面关闭了就删掉了. 刷新不会导致 sessionStorage 删除.
存储复杂数据 indexedDB
连接数据库
indexedDB 可以当作是一个非关系型数据库来用. 首先需要打开数据库.
let db: IDBDatabase;
function connect() {
return new Promise<void>((res, rej) => {
const request = window.indexedDB.open('TestDb', 1);
request.addEventListener('success', (event) => {
const req = event.target as IDBOpenDBRequest;
if (!req) return;
db = req.result;
res();
});
request.addEventListener('error', (event) => {
rej(new Error('数据库打开失败'));
});
request.addEventListener('upgradeneeded', (event) => {
db = (event.target as IDBOpenDBRequest).result;
const objectStore = db.createObjectStore('users', {
keyPath: 'id',
autoIncrement: true,
});
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('email', 'email', { unique: true });
});
});
}
IndexedDB 几乎都是基于 request 和事件的, 没有方便的 Promise.
这里 open 的 request 可以监听 success, error 和 upgradeneeded.
当数据库首次创建或者版本更新时, 会触发 upgradeneeded 事件, 并且比 success 事件早.
event.target.result 就是 IDBDatabase 了.
创建表
一般会在 upgradeneeded 事件创建对应的表.
request.addEventListener('upgradeneeded', (event) => {
db = (event.target as IDBOpenDBRequest).result;
const objectStore = db.createObjectStore('users', {
keyPath: 'id',
autoIncrement: true,
});
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('email', 'email', { unique: true });
});
IndexedDB 是非关系型数据库, 没有固定的字段. 最好给它加上索引, 可以根据索引查找.
增查删改
如果想要对数据库操作, 需要通过事务. 有三种事务:
- readonly
只读, 查询时使用
- readwrite
可读也可写
- versionchange
当需要修改数据库的模式或者结构(新建/删除对象存储, 索引等)时, 需要用这个.
这样开启一个事务:
const transaction = db.transaction(['store-name'], mode)
transaction 也有事件, complete 和 error.
增
const addUser = (user: User) => {
const transaction = db.transaction(['users'], 'readwrite');
transaction.addEventListener('complete', (e) => {
console.log('success', e);
});
transaction.addEventListener('error', (e) => {
console.error(e);
});
const objectStore = transaction.objectStore('users');
const request = objectStore.add(user);
request.onsuccess = (e) => {
console.log('用户添加成功', e);
};
request.onerror = () => {
console.error('添加用户失败', request.error);
};
};
查
加了索引, 就可以根据索引找数据了.
const getUserBy = (key: string, value: any) => {
const transaction = db.transaction(['users'], 'readonly');
const objectStore = transaction.objectStore('users');
const index = objectStore.index(key);
const request = index.getAll(value);
request.onsuccess = (e) => {
console.log('找到', e.target.result);
};
};
主键不需要使用 createIndex.
const transaction = db.transaction(['users'], 'readonly')
const objectStore = transaction.objectStore('users')
const request = objectStore.get(id)
request.onsuccess = () => {
console.log('获取用户成功', request.result)
}
request.onerror = () => {
console.error('获取用户失败', request.error)
}
删
const transaction = db.transaction(['users'], 'readwrite')
const objectStore = transaction.objectStore('users')
const request = objectStore.delete(id)
request.onsuccess = () => {
console.log('用户删除成功', id)
}
request.onerror = () => {
console.error('删除用户失败', request.error)
}
改
const updateUser = (user: User) => {
const transaction = db.transaction(['users'], 'readwrite');
const objectStore = transaction.objectStore('users');
const request = objectStore.put(user);
request.onsuccess = () => {
console.log('用户更新成功', user);
};
request.onerror = () => {
console.error('更新用户失败', request.error);
};
};
删和改都跟查差不多, 也可以通过索引来删除/修改数据.
过几天可能会封装一个IndexedDB的类, 如果有时间的话.