React 用 IndexedDB 做离线优先:借助 idb 把数据缓存在浏览器,断网也能用

test12026-11-020 次阅读

为什么 localStorage 不够用

前端想做离线优先,第一反应往往是 localStorage。它确实简单,但有两个硬伤:容量只有约 5MB,且只能存字符串。一旦要缓存列表、草稿、离线提交队列这类结构化且体量不小的数据,localStorage 很快就会不够。IndexedDB 是浏览器内置的事务型数据库,容量按磁盘剩余空间算,能存对象、二进制(Blob/ArrayBuffer),还支持索引查询。直接用原生 IndexedDB API 很啰嗦,社区里 idb 这个库把它包成了 Promise,写起来接近 async/await 的常规数据库操作。

用 idb 封装一个 Repository

把“打开数据库 + 建表 + 读写”收敛到一个模块,组件层只关心业务语义,不关心底层 API。

import { openDB } from 'idb';

const DB_NAME = 'app-cache';
const STORE = 'articles';

async function getDB() {
  return openDB(DB_NAME, 1, {
    upgrade(db) {
      if (!db.objectStoreNames.contains(STORE)) {
        const store = db.createObjectStore(STORE, { keyPath: 'id' });
        store.createIndex('by-updated', 'updatedAt');
      }
    },
  });
}

export async function cacheArticles(list) {
  const db = await getDB();
  const tx = db.transaction(STORE, 'readwrite');
  for (const item of list) {
    await tx.store.put({ ...item, updatedAt: Date.now() });
  }
  await tx.done;
}

export async function getCachedArticles() {
  const db = await getDB();
  return db.getAll(STORE);
}

export async function getCached(id) {
  const db = await getDB();
  return db.get(STORE, id);
}

在 React 里用自定义 Hook 读写

把读取逻辑包成 Hook,组件挂载时先读缓存,保证断网也有内容可渲染。

import { useEffect, useState } from 'react';
import { getCachedArticles, cacheArticles } from './db';

export function useArticles() {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let active = true;
    (async () => {
      // 1. 先读本地缓存,秒出
      const local = await getCachedArticles();
      if (active && local.length) setItems(local);

      // 2. 再请求网络,回填缓存
      try {
        const res = await fetch('/api/articles');
        const data = await res.json();
        if (active) setItems(data);
        await cacheArticles(data); // 写入 IndexedDB
      } catch (e) {
        // 断网:local 中已有内容,静默降级
      } finally {
        if (active) setLoading(false);
      }
    })();
    return () => { active = false; };
  }, []);

  return { items, loading };
}

离线优先的两条铁律

  • 读路径永远先落本地:打开页面第一帧就展示缓存,网络只是“刷新”而非“唯一来源”。
  • 写路径走“乐观写 + 离线队列”:本地先写成功,失败的请求塞进一个待发送队列,恢复网络后再重放,用 updatedAt 或版本号做冲突裁决。

这样即便地铁里信号全无,用户依然能看列表、能写草稿,恢复连接后数据自动同步。IndexedDB 不是银弹,它不擅长频繁小写的场景(那种仍该用内存 + 防抖落盘),但对“离线只读 + 批量回写”的资讯类应用,是最稳的底盘。

T

test1

文章作者

为什么 localStorage 不够用 前端想做离线优先,第一反应往往是 localStorage。它确实简单,但有...

分类
技术
发布时间
2026-11-02
字数
约 2280 字
阅读
0 次