1. 前言
localForage 是一款轻量且高效的 JavaScript 存储库,核心目标是优化 Web 应用的离线体验。它通过封装异步存储方案(IndexedDB 或 WebSQL),提供了与 localStorage
一致的简洁 API,同时解决了 localStorage
同步阻塞、存储容量有限、仅支持字符串类型的痛点。
在兼容性方面,localForage 具备自动降级能力:若浏览器不支持 IndexedDB 或 WebSQL,会自动 fallback 到 localStorage
,确保在各类浏览器环境中稳定运行(详细兼容性可参考 官方 Wiki)。
2. 快速上手:安装与基础使用
2.1 安装方式
localForage 支持多种引入方式,可根据项目场景选择:
方式1:直接引入 CDN
无需构建工具,直接在 HTML 中引入脚本:
html
<!-- 引入 localForage -->
<script src="https://cdn.jsdelivr.net/npm/localforage@1.10.0/dist/localforage.min.js"></script>
<!-- 基础使用 -->
<script>
// 存储数据
localforage.setItem('username', '张三', (err) => {
if (!err) {
// 读取数据
localforage.getItem('username', (err, value) => {
console.log('用户名:', value); // 输出:用户名:张三
});
}
});
</script>
方式2:npm 安装(模块化项目)
适用于 Vue、React、TypeScript 等模块化项目:
bash
# 安装依赖
npm install localforage --save
在项目中引入:
javascript
// ES Module 引入
import localforage from 'localforage';
// CommonJS 引入
const localforage = require('localforage');
2.2 核心 API 示例
localForage 的 API 设计与 localStorage
高度一致,但全部为异步操作,支持「回调函数」「Promise」「async/await」三种调用方式,推荐优先使用 Promise 或 async/await(代码更简洁)。
2.2.1 存储数据(setItem)
javascript
// 1. 回调函数方式
localforage.setItem('user', { id: 1, name: '李四' }, (err) => {
if (err) console.error('存储失败:', err);
});
// 2. Promise 方式
localforage.setItem('token', 'abc123')
.then(() => console.log('token 存储成功'))
.catch(err => console.error('存储失败:', err));
// 3. async/await 方式(推荐)
async function saveData() {
try {
await localforage.setItem('theme', 'dark');
console.log('主题存储成功');
} catch (err) {
console.error('存储失败:', err);
}
}
saveData();
2.2.2 读取数据(getItem)
javascript
async function getData() {
try {
const user = await localforage.getItem('user');
const token = await localforage.getItem('token');
console.log('用户信息:', user); // 输出:{ id: 1, name: '李四' }
console.log('Token:', token); // 输出:abc123
} catch (err) {
console.error('读取失败:', err);
}
}
getData();
2.2.3 删除数据(removeItem)
javascript
async function deleteData() {
try {
await localforage.removeItem('token');
console.log('token 已删除');
} catch (err) {
console.error('删除失败:', err);
}
}
deleteData();
2.2.4 清空所有数据(clear)
javascript
async function clearAll() {
try {
await localforage.clear();
console.log('所有数据已清空');
} catch (err) {
console.error('清空失败:', err);
}
}
clearAll();
2.2.5 获取键名列表(keys)与数据总数(length)
javascript
async function getStorageInfo() {
try {
// 获取所有键名
const keys = await localforage.keys();
console.log('所有键名:', keys); // 输出:['user', 'theme']
// 获取数据总数
const count = await localforage.length();
console.log('数据总数:', count); // 输出:2
} catch (err) {
console.error('获取信息失败:', err);
}
}
getStorageInfo();
3. 核心特性:突破 localStorage 限制
3.1 支持多类型数据存储
与 localStorage
仅支持字符串不同,localForage 可直接存储多种类型数据,无需手动 JSON.stringify
/JSON.parse
:
- 基础类型:字符串、数字、布尔值、null、undefined
- 复杂类型:对象、数组
- 二进制类型:ArrayBuffer、Blob、TypedArray(如 Uint8Array)
示例:存储 Blob 类型(图片、文件)
javascript
// 假设已获取图片 Blob 对象
async function saveImage(blob) {
try {
await localforage.setItem('avatar', blob);
console.log('图片存储成功');
// 读取并显示图片
const savedBlob = await localforage.getItem('avatar');
const imgUrl = URL.createObjectURL(savedBlob);
document.querySelector('#avatar').src = imgUrl;
} catch (err) {
console.error('图片存储失败:', err);
}
}
3.2 自定义配置(config)
通过 config()
方法可设置数据库的基础信息(如驱动类型、数据库名、存储容量等),需在所有数据操作前调用。
支持的配置项:
配置项 | 说明 | 可选值 |
---|---|---|
driver |
强制指定存储驱动 | localforage.INDEXEDDB /WEBSQL /LOCALSTORAGE |
name |
数据库名称 | 字符串(默认:localforage ) |
version |
数据库版本号 | 数字(默认:1.0) |
size |
数据库容量(仅 WebSQL 支持) | 数字(单位:字节,默认:4980736) |
storeName |
存储对象名称(类似表名,需字母数字+下划线) | 字符串(默认:keyvaluepairs ) |
description |
数据库描述 | 字符串(默认:空) |
示例:强制使用 WebSQL 并自定义数据库名
javascript
// 配置需在数据操作前调用
localforage.config({
driver: localforage.WEBSQL,
name: 'myAppDB', // 数据库名
version: 2.0,
size: 10 * 1024 * 1024, // 10MB 容量
storeName: 'userData',
description: '存储我的应用用户数据'
});
// 后续操作将基于上述配置
await localforage.setItem('user', { id: 2 });
3.3 多实例隔离(createInstance)
通过 createInstance()
可创建多个独立的 localForage 实例,不同实例的存储数据完全隔离(避免键名冲突),每个实例可单独配置。
示例:创建两个隔离的实例
javascript
// 创建实例1:存储用户数据
const userStore = localforage.createInstance({
name: 'userDB',
storeName: 'userInfo'
});
// 创建实例2:存储应用配置
const configStore = localforage.createInstance({
name: 'configDB',
storeName: 'appConfig'
});
// 实例1 存储数据
await userStore.setItem('name', '王五');
// 实例2 存储数据(键名相同但不冲突)
await configStore.setItem('name', '我的应用');
// 读取数据:各自独立
console.log(await userStore.getItem('name')); // 输出:王五
console.log(await configStore.getItem('name')); // 输出:我的应用
4. 框架与工具支持
4.1 TypeScript 支持
localForage 原生支持 TypeScript,需根据 tsconfig.json
中的 allowSyntheticDefaultImports
配置选择引入方式:
情况1:allowSyntheticDefaultImports: true
(推荐,TypeScript 1.8+ 支持)
typescript
import localForage from 'localforage';
async function saveUser() {
interface User {
id: number;
name: string;
}
const user: User = { id: 3, name: '赵六' };
await localForage.setItem<User>('user', user); // 类型校验
}
情况2:allowSyntheticDefaultImports: false
typescript
// 方式1:整体导入
import * as localForage from 'localforage';
// 方式2:CommonJS 导入
import localForage = require('localforage');
4.2 框架专用驱动
localForage 为主流前端框架提供了专用驱动,可直接集成框架的模型(Model)进行离线存储,无需手动处理数据转换:
框架 | 支持情况 |
---|---|
AngularJS | 有专用驱动(localforage-angular) |
Angular 4+ | 有专用驱动(ngx-localforage) |
Backbone | 有专用驱动(backbone.localforage) |
Ember | 有专用驱动(ember-localforage) |
Vue | 有专用驱动(vue-localforage) |
NuxtJS | 有专用驱动(nuxt-localforage) |
若需集成其他框架,可查看 官方 Wiki 或提交 issue 申请添加。
4.3 RequireJS 支持
在使用 RequireJS 的传统项目中,可通过模块化方式引入 localForage:
javascript
define(['localforage'], function(localforage) {
// 回调函数方式
localforage.setItem('key1', 'value1', (err) => {
if (!err) console.log('存储成功');
});
// Promise 方式
localforage.setItem('key2', 'value2')
.then(() => console.log('存储成功'));
});
5. 高级特性:自定义驱动
若内置的 IndexedDB/WebSQL/localStorage 无法满足需求(如对接自定义存储服务),可通过 defineDriver()
自定义驱动。
核心步骤:
- 定义驱动对象(需实现
_initStorage
、_getItem
、_setItem
等核心方法); - 通过
localforage.defineDriver()
注册驱动; - 使用
setDriver()
切换到自定义驱动。
示例(简化版自定义驱动):
javascript
// 1. 定义自定义驱动(此处为模拟内存存储)
const memoryDriver = {
_storage: {}, // 内存存储容器
driver: 'MEMORY_DRIVER', // 驱动标识(唯一)
// 初始化存储
_initStorage(options) {
this._storage[options.storeName] = this._storage[options.storeName] || {};
return Promise.resolve();
},
// 读取数据
_getItem(key, callback) {
const store = this._storage[localforage.config().storeName];
const value = store[key];
callback(null, value);
},
// 存储数据
_setItem(key, value, callback) {
const store = this._storage[localforage.config().storeName];
store[key] = value;
callback(null);
},
// 其他需实现的方法:_removeItem、_clear、_keys、_length
_removeItem(key, callback) { /* 实现逻辑 */ },
_clear(callback) { /* 实现逻辑 */ },
_keys(callback) { /* 实现逻辑 */ },
_length(callback) { /* 实现逻辑 */ }
};
// 2. 注册驱动
localforage.defineDriver(memoryDriver)
.then(() => {
// 3. 切换到自定义驱动
return localforage.setDriver('MEMORY_DRIVER');
})
.then(() => {
// 使用自定义驱动存储数据
return localforage.setItem('test', '内存存储值');
})
.then(() => {
return localforage.getItem('test');
})
.then(value => {
console.log('读取结果:', value); // 输出:内存存储值
});
更多自定义驱动细节可参考 官方 API 文档。
6. 开发与测试
6.1 本地开发环境搭建
若需参与 localForage 源码开发,需安装依赖并配置环境:
bash
# 1. 全局安装 bower(依赖管理工具)
npm install -g bower
# 2. 克隆仓库(替换 USERNAME 为你的 GitHub 用户名)
git clone git@github.com:USERNAME/localForage.git
cd localForage
# 3. 安装 npm 依赖(构建、测试工具)
npm install
# 4. 安装 bower 依赖(测试相关依赖)
bower install
6.2 运行测试
localForage 的测试需在浏览器环境中运行,本地测试依赖 PhantomJS(无头浏览器):
bash
# 运行所有测试(包括 lint 检查)
npm test
# 仅运行单元测试(跳过 lint)
grunt test
提交 Pull Request 时,Travis CI 会通过 Sauce Labs 在所有支持的浏览器中自动运行测试,确保兼容性。
7. 性能与体积
localForage 体积轻量,对应用加载性能影响极小:
构建版本 | 体积 |
---|---|
未压缩 | ~70kB |
压缩后 | ~29kB |
gzip 压缩 | ~8.8kB |
Brotli 压缩 | ~7.8kB |
即使在网络较差的环境中,也能快速加载,不会成为应用的性能瓶颈。
8. 总结与适用场景
8.1 核心优势
- 简洁 API :与
localStorage
一致,学习成本低; - 异步非阻塞:避免同步存储导致的页面卡顿;
- 多类型支持:直接存储对象、Blob 等,无需手动转换;
- 自动降级:兼容所有浏览器,无需处理兼容性逻辑;
- 多实例隔离:支持独立存储空间,避免键名冲突;
- 轻量体积:gzip 压缩后仅 ~8.8kB,对应用体积影响小。
8.2 适用场景
- 离线应用数据存储(如 PWA 离线缓存);
- 需存储大量数据(超过
localStorage
5MB 限制); - 需存储二进制数据(如图片、文件);
- 对存储性能要求高(异步操作不阻塞 UI);
- 多模块/多应用共享同一域名下的存储(通过多实例隔离)。
8.3 注意事项
config()
需在所有数据操作前调用,否则配置不生效;- 使用 localStorage 作为降级方案时,仍受 5MB 容量限制,不建议存储大量 Blob;
- 多实例的
name
或storeName
需唯一,避免数据混淆; - 自定义驱动需实现所有核心方法,确保功能完整性。
通过 localForage,开发者可轻松实现高效、兼容的 Web 存储功能,大幅简化离线应用的开发流程。
本次分享就到这儿啦,我是鹏多多,深耕前端的技术创作者,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~
PS:在本页按F12,在console中输入document.getElementsByClassName('panel-btn')[0].click();有惊喜哦~
往期文章
- 今天你就是VS Code之神!15个隐藏技巧让代码效率翻倍
- React无限滚动插件react-infinite-scroll-component的配置+优化+避坑指南
- 前端音频兼容解决:音频神器howler.js从基础到进阶完整使用指南
- 使用React-OAuth进行Google/GitHub登录的教程和案例
- 纯前端人脸识别利器:face-api.js手把手深入解析教学
- 关于React父组件调用子组件方法forwardRef的详解和案例
- React跨组件数据共享useContext详解和案例
- Web图像编辑神器tui.image-editor从基础到进阶的实战指南
- 开发个人微信小程序类目选择/盈利方式/成本控制与服务器接入指南
- 前端图片裁剪Cropper.js核心功能与实战技巧详解
- 编辑器也有邪修?盘点VS Code邪门/有趣的扩展
- js使用IntersectionObserver实现目标元素可见度的交互
- Web前端页面开发阿拉伯语种适配指南
- 让网页拥有App体验?PWA 将网页变为桌面应用的保姆级教程PWA
- 使用nvm管理node.js版本以及更换npm淘宝镜像源
- 手把手教你搭建规范的团队vue项目,包含commitlint,eslint,prettier,husky,commitizen等等