1. 为什么前端离线缓存绕不开 IndexedDB做离线优先的 Web 应用时localStorage 的 5MB 上限和同步阻塞特性很快就会成为瓶颈。IndexedDB 作为浏览器内置的事务型 NoSQL 数据库能存结构化克隆算法支持的任何对象容量按磁盘配额走是 PWA、离线笔记、本地草稿箱这类场景的标配。但它的 API 全是异步回调事务、游标、索引三件套叠在一起新手写起来很容易卡在「数据拿不到」「回调里 this 丢了」这些坑上。这篇内容聚焦一个具体场景你用 IndexedDB 做前端本地数据缓存同时需要把本地读写结果通过统一的 API 通道回传或拉取远端配置做联调。我会给出可直接复制的增删改查代码片段以及 TaoToken 统一 Key 接入的配置骨架最后用浏览器 DevTools 一步步验证本地库操作和接口请求是否都通了。适合已经会写基础 JS、想快速把 IndexedDB 跑起来并完成一次接口联调的前端开发者。2. TaoToken 前置准备统一 Key 与通道配置TaoToken 在这里的角色是统一 API 通道帮你把模型对话、编码辅助等能力收敛到一个 Key 上前端调试本地存储时如果需要调用远端接口做数据校验或生成测试数据不用到处切换配置。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。你需要先拿到一个 API Key。进入控制台的 API Keys 页面创建https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建后复制那串以 sk- 开头的字符串前端调试阶段建议放在 .env.local 里不要硬编码进提交到仓库的源码。如果你后续要做长期编码或 Agent 类任务可以了解 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。纯做模型验证的话模型对话页更直接https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。注意前端直接暴露 Key 只适合本地调试。生产环境务必走你自己的后端代理把 Key 留在服务端。3. 可复制配置IndexedDB 增删改查完整骨架先封装一个打开数据库的函数把建表和索引逻辑放在 onupgradeneeded 里。下面以 student 表为例主键用 idname 建唯一索引。// db.js const DB_NAME offline_cache; const DB_VERSION 1; export function openDB() { return new Promise((resolve, reject) { const request indexedDB.open(DB_NAME, DB_VERSION); request.onerror () reject(request.error); request.onsuccess () resolve(request.result); request.onupgradeneeded (event) { const db event.target.result; if (!db.objectStoreNames.contains(student)) { const store db.createObjectStore(student, { keyPath: id }); store.createIndex(name, name, { unique: true }); } }; }); }新增和修改都用 put区别在于 put 是「有则覆盖、无则新增」add 在 key 已存在时会报错。下面这个函数把事务和请求包成 Promise避免回调嵌套。// crud.js import { openDB } from ./db.js; export async function upsertStudent(student) { const db await openDB(); return new Promise((resolve, reject) { const tx db.transaction([student], readwrite); const store tx.objectStore(student); const request store.put(student); request.onsuccess () resolve(request.result); request.onerror () reject(request.error); }); } export async function deleteStudent(id) { const db await openDB(); return new Promise((resolve, reject) { const tx db.transaction([student], readwrite); const store tx.objectStore(student); const request store.delete(id); request.onsuccess () resolve(); request.onerror () reject(request.error); }); }查询分三种常用姿势主键 get、全量 getAll、索引条件查询。索引查询是 IndexedDB 相对别扭的地方必须用 IDBKeyRange 构造范围。export async function getStudentById(id) { const db await openDB(); return new Promise((resolve, reject) { const tx db.transaction([student], readonly); const store tx.objectStore(student); const request store.get(id); request.onsuccess () resolve(request.result); request.onerror () reject(request.error); }); } export async function getAllStudents() { const db await openDB(); return new Promise((resolve, reject) { const tx db.transaction([student], readonly); const store tx.objectStore(student); const request store.getAll(); request.onsuccess () resolve(request.result); request.onerror () reject(request.error); }); } export async function getStudentsByName(name) { const db await openDB(); return new Promise((resolve, reject) { const tx db.transaction([student], readonly); const store tx.objectStore(student); const index store.index(name); const keyRange IDBKeyRange.only(name); const request index.getAll(keyRange); request.onsuccess () resolve(request.result); request.onerror () reject(request.error); }); }TaoToken 的调用骨架放在单独模块用 fetch 走统一基址。这里以模型对话接口为例实际路径以接入文档为准https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。// taotoken.js const BASE_URL https://taotoken.net/api; const API_KEY import.meta.env.VITE_TAOTOKEN_KEY; export async function chatOnce(prompt) { const res await fetch(${BASE_URL}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${API_KEY} }, body: JSON.stringify({ model: claude-sonnet-4-20250514, messages: [{ role: user, content: prompt }] }) }); if (!res.ok) throw new Error(HTTP ${res.status}); return res.json(); }4. 验证请求DevTools 里跑通本地库与接口联调先写一个联调脚本把本地写入和远端请求串起来。逻辑是往 IndexedDB 写一条学生记录读出来再把读到的数据拼成 prompt 发给 TaoToken最后把返回结果写回本地。// main.js import { upsertStudent, getAllStudents } from ./crud.js; import { chatOnce } from ./taotoken.js; async function run() { await upsertStudent({ id: 1, name: 张三, score: 90 }); const list await getAllStudents(); console.log(本地读取:, list); const reply await chatOnce(请给这条学生数据写一句评语${JSON.stringify(list[0])}); console.log(接口返回:, reply); await upsertStudent({ id: 2, name: 李四, score: 85, comment: reply.choices?.[0]?.message?.content }); console.log(回写完成:, await getAllStudents()); } run().catch(console.error);打开 Chrome DevTools切到 Application 面板左侧 Storage 下找到 IndexedDB展开 offline_cache - student能看到 id 为 1 和 2 的记录。切到 Network 面板筛选 Fetch/XHR刷新页面后能看到一条发往 taotoken.net 的 POST 请求状态码 200Response 里有模型返回内容。Console 面板会依次打印「本地读取」「接口返回」「回写完成」三段日志。如果 Network 里请求是 401说明 Key 没读到或格式不对检查 .env.local 的变量名是否和 import.meta.env 里一致。如果 IndexedDB 里看不到表多半是 onupgradeneeded 没触发把 DB_VERSION 加 1 再刷新即可。5. 本篇常见错排查回调里 this 丢失IndexedDB 的成功回调里 this 指向 request 对象不是你的组件实例。用箭头函数或在外部提前存 const self this更推荐直接用 Promise 封装从根上绕开。在 onsuccess 外面取值拿到 undefinedIndexedDB 是异步的request.result 只有在 onsuccess 触发后才有值。所有依赖查询结果的操作都必须写在回调或 await 之后。add 报 ConstraintError主键重复或唯一索引冲突。想覆盖就用 put想严格新增就先用 get 判断。索引查询返回空检查 createIndex 时字段名是否和对象属性完全一致IDBKeyRange.only 的值类型也要匹配字符串和数字不能混。事务自动提交导致后续请求失败一个事务里如果中间有 await 让出主线程事务可能已经提交再发请求会报 TransactionInactiveError。把同一事务内的操作同步发出或每个操作单独开事务。版本号没变导致建表逻辑不执行onupgradeneeded 只在版本升高时触发。改了表结构记得把 DB_VERSION 递增。6. 继续联调与下一步本地库跑通后你可以把 getAllStudents 的结果批量发给 TaoToken 做数据清洗或补全再把结果 put 回 IndexedDB形成一个离线优先的闭环。接口路径和参数以接入文档为准https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。需要新建或轮换 Key 时去 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。想先在网页上验证模型返回格式用模型对话页最快https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。长期做编码类任务再考虑 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。实测下来IndexedDB 最省心的写法就是全部 Promise 化别在回调里手动管 this。游标那套 openCursor continue 在需要边遍历边过滤时才有优势单纯取全量直接用 getAll 更清爽。
企业数字化 ERP 产品动态
相关推荐
K8s管理RK3588 NPU:Device Plugin实现边缘AI算力调度 1. 缘起:一块被“闲置”的算力手里有一块 RK3588 的板子,6 TOPS 的 NPU 算力,跑 YOLOv8 推理能到几十帧,功耗还低得感人。这东西放在边缘侧做视觉推理,性价比几乎是无敌的。但问题来了——当你手里不止一块板子&#x… · 2026/9/26 11:50:58
Camera HAL — ResourceManager总结 QCOM Camera HAL — ResourceManager总结 面向:开发 / 学习 / 问题定位 一、总览维度对比表
ResourceManager 在 CAMX 与 CHI 两层各有一个,但职责和层级不同。先建立整体心智模型: 维度 CAMX ResourceManager CHI ChiResourceManager 角色 硬件资源账本(真实持有/扣减资源… · 2026/9/26 11:50:58
Genkit多回合AI代理实战:TypeScript与Firestore会话管理 1. 为什么多回合AI代理值得你花时间折腾多回合AI代理这个概念,这两年在开发者圈子里被讨论得越来越多。简单说,它就是一个能记住上下文、能连续对话、能根据历史信息做决策的AI程序。跟那种问一句答一句的“单次问答”不一样,多回合代理更像是… · 2026/9/26 11:50:51
终于把进程和线程学会了:用 TaoToken 统一 Key 打通多工具调试配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 16:25:33
程力专用汽车救护车联系电话投诉途径解析 负压监护型转运车配置 行业发展背景与企业业务概况随着我国基层医疗体系建设不断推进,以及公共卫生应急保障能力要求持续提升,医疗专用车行业迎来了稳步增长的发展阶段。从日常基层医疗筛查、公共卫生服务下乡,到突发公共卫生事件的应急转运、灾害现场的医疗救援&a… · 2026/9/26 16:25:27
从医疗到电商,AI 搜索如何重构产业价值?附真实案例与数据 在数字信息呈指数级增长、用户注意力成为稀缺资源的当下,AI搜索已完成从辅助工具到产业变革核心动力的蜕变。相较于传统搜索依赖“关键词匹配”的浅层逻辑,新一代AI搜索依托深度学习驱动的语义理解、多维度知识图谱构建等核心技术,实现了从“… · 2026/9/26 16:25:21
Java程序员轻松转型AI Agent:收藏这份保姆级学习路线,稳拿高薪Offer! 本文详细介绍了Java程序员如何顺利转型AI Agent开发。作者从自身经验出发,提供了从认知阶段到工程化落地的完整学习路线,包括打通认知、Prompt工程、RAG技术、Agent核心能力及工程化部署等五个阶段,帮助读者系统学习并掌握AI Agent开发技能。… · 2026/9/26 16:25:21
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46