资讯动态

四、浏览器存储

发布时间:2026/8/19 12:53:04 来源:尧图企业网站定制
一、Web StorageWeb 存储1. 概念Web StorageWeb 存储是 HTML5 提供的一种在浏览器本地存储数据的机制。你可以把它理解为浏览器提供的一个“本地小仓库”。它最大的特点是数据只保存在用户的浏览器里不会像 Cookie 那样在每次请求时都发给服务器所以它更安全也不会浪费网络带宽。2. 特征Web Storage 主要分为localStorage和sessionStorage它们有以下几个共同和不同的特征同源性限制只有在同一个域名下才能访问。比如a.com存的数据b.com是看不到的。只能存字符串这是新手最容易踩坑的地方如果你存一个对象Object或数组它会自动变成字符串[object Object]。必须用JSON.stringify()转成字符串再存取出来时用JSON.parse()转回对象。容量较大通常每个域名有 5MB 左右的存储空间比 Cookie 的 4KB 大多了。生命周期不同localStorage永久有效除非手动删除。sessionStorage关闭当前标签页即失效。3. 使用场景存储类型适用场景典型示例localStorage用户个人的、不需要频繁更新的、希望下次打开还在的数据深色模式 / 字体大小设置、记住用户名、缓存不常变动的列表数据sessionStorage临时的、只在当前操作流中有效的数据多步表单的临时数据、搜索结果页面的过滤条件、防止表单重复提交的临时标识4. 使用方法它们提供的方法非常简单只有这几个setItem存、getItem取、removeItem删、clear清空。场景一使用 localStorage 记住用户的“夜间模式”偏好假设我们做了一个博客用户点击按钮切换到夜间模式我们希望他下次打开还是夜间模式。// 1. 存储数据存// 假设用户点击了切换按钮constthemedark;localStorage.setItem(userTheme,theme);// 2. 读取数据取// 在网页刚加载时我们读取这个设置constsavedThemelocalStorage.getItem(userTheme);if(savedThemedark){document.body.classList.add(dark-mode);// 给网页加上夜间模式样式}// 3. 删除数据删// 如果用户想恢复默认设置localStorage.removeItem(userTheme);场景二使用 sessionStorage 暂存多步表单假设你在做一个“发布文章”的功能第一步填标题第二步填正文。为了防止用户在第二步不小心刷新页面导致第一步填的标题没了// 第一步用户填完标题点击“下一步”时暂存consttitledocument.getElementById(titleInput).value;sessionStorage.setItem(draftTitle,title);// 第二步在正文页面的初始化代码中检查有没有草稿constdraftTitlesessionStorage.getItem(draftTitle);if(draftTitle){document.getElementById(titleInput).valuedraftTitle;// 自动填回标题}5. 避坑指南如何存储对象在实际开发中我们很少只存简单的字符串经常要存对象或数组。记住这个万能公式constuserInfo{name:张三,age:18};// ❌ 错误写法直接存对象localStorage.setItem(user,userInfo);// 存进去会变成 [object Object]// ✅ 正确写法存之前转成 JSON 字符串localStorage.setItem(user,JSON.stringify(userInfo));// ✅ 正确写法取出来时解析回对象constuserStrlocalStorage.getItem(user);constrealUserJSON.parse(userStr);console.log(realUser.name);// 正常输出 张三其他常见坑点容量限制虽然单个域名有 5MB 左右但超出后会报错建议在存储大量数据时做异常处理。隐私模式部分浏览器在隐私模式下可能无法写入 localStorage或者容量被限制使用前最好做一下可用性检测。同步操作setItem/getItem是同步的大量数据读写可能会阻塞主线程复杂场景建议配合 IndexedDB 使用。二、Cookies饼干1. 概念Cookie饼干是服务器发送给浏览器并保存在本地的一小块数据通常不超过 4KB。你可以把它理解为“服务器发给你的一张专属身份凭证或记事本”。它和 Web Storage 最大的区别在于只要是你向该服务器发送网络请求浏览器都会自动把 Cookie 带上服务器一看到就知道你是谁、你之前做过什么。2. 特征自动携带每次发起 HTTP 请求时浏览器会自动在请求头Request Header中带上相关的 Cookie无需手动添加。容量极小单个 Cookie 的大小通常限制在 4KB 左右不适合存大量数据。生命周期可控如果不设置过期时间关闭浏览器后它就会自动消失会话级 Cookie。如果设置了过期时间它就会一直存在直到过期持久化 Cookie。安全性配置支持HttpOnly防止前端 JS 读取防 XSS 攻击、Secure只允许 HTTPS 传输、SameSite防止跨站请求伪造 CSRF等安全属性。3. 使用场景使用场景说明典型示例用户登录状态会话管理最核心的用途。用户登录后服务器发一个 Token 存在 Cookie 里后续请求自动携带服务器便能识别用户身份。登录后访问个人中心、订单页等需要鉴权的页面购物车记录未登录状态下往购物车加商品服务器用 Cookie 记住这些商品登录后再合并到账号下。电商网站的未登录购物车个性化设置记住用户的语言、主题等偏好服务器在返回页面时就按偏好渲染。中英文切换、网站主题颜色行为追踪广告商或分析工具记录用户的浏览轨迹用于推送精准广告或统计分析。百度统计、Google Analytics4. 使用方法 场景一原生 JavaScript 操作 Cookie原生操作就是通过document.cookie这个属性。// 1. 创建/修改 Cookie// 格式名字值; expires过期时间; path生效路径constexpireDatenewDate();expireDate.setDate(expireDate.getDate()7);// 7天后过期document.cookieusername张三; expiresexpireDate.toUTCString(); path/;// 2. 读取 Cookie// 注意document.cookie 会把当前域名下所有的 Cookie 拼成一个长字符串返回console.log(document.cookie);// 输出: username张三// 3. 删除 Cookie// 原理把过期时间设置为过去的时间浏览器就会自动删掉它document.cookieusername张三; expiresThu, 01 Jan 1970 00:00:00 UTC; path/;注意原生读取时需要自己写正则或split去截取想要的值非常麻烦所以实际开发中极少这么干。 场景二实际开发中的标准姿势使用 js-cookie 库在实际企业级项目中我们通常会安装一个轻量级库js-cookie它把复杂的原生 API 封装得极其简单npminstalljs-cookieimportCookiesfromjs-cookie;// 1. 设置 Cookie超级简单支持直接传对象支持设置天数Cookies.set(userToken,abc123456,{expires:7,secure:true});// 2. 读取 CookieconsttokenCookies.get(userToken);// 直接拿到 abc123456// 3. 删除 CookieCookies.remove(userToken);5. Cookies 配置核心基础配置决定门票的归属Name名称和 Value值最基础的键值对比如tokenabc123。Domain域名规定这张门票在哪个游乐园生效。比如设置为.example.com那么www.example.com和api.example.com都能使用这张门票支持子域名共享。如果不设置默认就是当前访问的完整域名。Path路径规定门票在游乐园的哪些区域有效。比如设置为/admin那么只有访问/admin/xxx时才会带上这个 Cookie如果是/则全站有效。Expires / Max-Age过期时间门票的有效期。如果设置了具体的过期时间比如 7 天后浏览器会把它保存在硬盘上即使关闭浏览器再打开门票依然有效持久化 Cookie。如果不设置这个属性浏览器一关闭门票就自动销毁会话 Cookie。安全配置防止门票被偷或伪造在实际开发中这部分极其重要HttpOnly设置为true后这张门票就禁止前端 JavaScript 读取即document.cookie拿不到它。这能极大防止黑客通过 XSS 攻击偷走用户的登录凭证。它只允许浏览器在发请求时默默带上。Secure设置为true后这张门票只允许在 HTTPS 加密通道下传输。如果用户不小心访问了 HTTP 的链接浏览器绝不会把这个 Cookie 发出去防止在公共 WiFi 下被窃听。SameSite同站限制用来防止 CSRF跨站请求伪造攻击。它有三个值取值说明Strict最严格完全不允许跨站携带。Lax默认推荐值允许部分安全的跨站请求比如点击链接跳转携带。None允许跨站携带但前提是必须同时设置Secure: true。现代进阶配置应对复杂的追踪场景PartitionKey分区键这是比较新的特性。为了防止第三方 Cookie 跨站追踪用户隐私浏览器引入了分区存储。带有这个配置的 Cookie只有在特定的顶级网站下才会生效换个顶级网站就失效了从而保护用户隐私。6. 使用指南别把 Cookie 当大仓库超过 4KB 的数据或者不需要发给服务器的数据如主题颜色、草稿请果断使用localStorage。安全第一千万不要在 Cookie 里明文存储用户的密码如果是存登录凭证Token一定要让后端加上HttpOnly和Secure属性防止黑客通过脚本偷走你的 Cookie三、Cookies、localStorage、sessionStorage 大横比为了让你更直观地理解三者的差异我将它们的关键特性整理成了一张对比表特性维度CookielocalStoragesessionStorage存储容量极小约 4KB较大约 5MB较大约 5MB生命周期可设置过期时间未设置则浏览器关闭失效永久存储除非手动清除仅在当前标签页有效关闭即销毁是否随请求自动发送✅ 是每次请求都会携带❌ 否仅存本地❌ 否仅存本地作用域访问范围同源窗口共享可通过 Domain/Path 限制同源的所有标签页共享仅限当前标签页同源也不共享安全性中高可设置 HttpOnly 防 XSSSameSite 防 CSRF低极易受 XSS 攻击窃取低极易受 XSS 攻击窃取API 易用性较差原生需自行拼接字符串极佳简单的键值对 API极佳同 localStorage核心定位与服务器通信的载体本地持久化存储仓库单次会话的临时草稿纸简单总结Cookie 是为了“沟通”而生localStorage 是为了“记住”而生sessionStorage 是为了“临时办事”而生。四、Token 到底应该存在哪里在实际项目中登录成功后拿到的 Token如 JWT该放在哪里直接关系到应用的安全性和开发体验。下面详细分析三种主流方案。1. 存在 Cookie 中推荐HttpOnly Secure SameSite这是安全性最高的方案。优点通过设置HttpOnly前端 JavaScript 无法读取 Token这从根本上杜绝了 XSS跨站脚本攻击窃取 Token 的风险。同时浏览器会自动携带无需前端手动处理。缺点由于浏览器会自动带上 Cookie它容易遭受 CSRF跨站请求伪造攻击需要后端配合SameSite属性或 CSRF Token 进行防御。此外前后端分离的跨域场景配置相对复杂。适用场景对安全性要求极高的系统如金融、银行网站以及传统的同域前后端应用。核心原则前端只负责“触发”后端负责“安全交付与验证”。这种方案不能由前端直接用 JavaScript 设置因为 JS 无法设置 HttpOnly 属性而是需要前后端紧密配合。以下是完整的落地实现方案第一步后端安全交付 Token核心当用户登录成功或 OAuth 授权回调完成后后端绝不能把 Token 放在 JSON 响应体里返回给前端而是应该通过 HTTP 响应头Set-Cookie将 Token 写入 Cookie并加上“安全三连”配置。Node.js (Express) 示例// 登录成功后后端签发 Token 并写入 Cookieres.cookie(auth_token,token,{httpOnly:true,// 核心禁止前端 JS 读取防 XSSsecure:true,// 核心强制仅在 HTTPS 下传输sameSite:Strict,// 核心限制跨站请求防 CSRFmaxAge:3600000,// 设置过期时间如 1 小时path:/// 全站有效});// 返回成功状态但不包含 Token 明文res.json({code:200,message:登录成功});第二步前端发起请求无需手动带 Token因为 Token 存在 HttpOnly Cookie 中浏览器在发起同域请求时会自动携带这个 Cookie前端完全不需要也无法手动把它加到请求头里。前端 Axios 请求示例// 发起请求时只需确保携带凭证fetch(/api/user/info,{credentials:include// 关键允许跨域时携带 Cookie});第三步防御 CSRF 攻击双重保险虽然SameSiteStrict已经能防住绝大多数 CSRF 攻击但在高安全场景下通常还会叠加 CSRF Token 机制后端下发在登录时后端额外生成一个 CSRF Token放在普通 CookiehttpOnly: false中返回让前端 JS 可以读取。前端携带前端在发起修改数据的请求POST/PUT/DELETE时从普通 Cookie 中读出 CSRF Token并放入请求头如X-CSRF-Token中。后端校验后端同时校验 Cookie 中的auth_token和请求头中的X-CSRF-Token是否匹配。由于恶意网站无法读取你域名下的普通 Cookie也就无法伪造这个请求头。 跨域场景的特殊处理如果你的前端如www.example.com和后端如api.example.com不在同一个域名下后端设置 Cookie 时必须将sameSite设置为None并且必须配合secure: true。后端必须配置 CORS 响应头允许前端域名访问并设置Access-Control-Allow-Credentials: true。前端请求时必须加上credentials: include。2. 存在 localStorage 中目前最主流这是目前前后端分离架构下最常见的方案。优点跨域非常灵活不受 Cookie 的 Domain 限制天然免疫 CSRF 攻击。前端管理 Token如刷新、清除非常方便。缺点安全性较弱。只要网站存在 XSS 漏洞恶意脚本就可以直接通过localStorage.getItem(token)把 Token 偷走。适用场景绝大多数前后端分离项目、中后台管理系统、ToC 产品等。前提是必须做好严格的 XSS 防护如配置 CSP 策略、对用户输入进行转义。3. 存在 sessionStorage 中特点生命周期极短关闭标签页就消失。适用场景仅适用于“阅后即焚”或临时会话场景。例如用户在一个新标签页中执行一次敏感操作如修改密码验证通过后生成一个临时 Token操作完成或关闭页面后立即失效防止 Token 被长期留存带来的风险。五、IndexedDB索引数据库1. 概念IndexedDB 是一种嵌入在浏览器环境中的客户端 NoSQL 数据库。它允许网页在用户的本地存储和操作大量结构化数据。你可以把它理解为浏览器里的一个“本地微型数据库”支持事务、索引查询并且操作是异步的绝对不会让你的网页卡顿。与 Web Storage 不同IndexedDB 不是简单的键值对存储而是一个完整的、具备事务机制的数据库它能够处理复杂的查询和海量的数据是构建现代 Web 应用的重要基石。2. 特征大容量它的存储空间通常不少于 250MB甚至没有明确上限远超 Cookie 和 LocalStorage。这使得它非常适合存储离线应用所需的大量数据。异步操作所有的增删改查都是异步的不会阻塞主线程非常适合处理海量数据。即使你在读写几万条数据页面依然可以流畅滚动和交互。支持事务它支持只读、读写等事务模式。如果一组操作中有一个失败了所有的操作都会回滚保证数据的一致性。就像关系型数据库一样事务是 IndexedDB 保证数据完整性的核心机制。支持索引这是它叫“Indexed”的原因。你可以为数据中的某个属性建立索引从而实现快速查询就像数据库里的索引一样。比如你可以为“创建时间”建索引快速按时间排序查询。存储类型丰富不仅能存字符串还能直接存储复杂的 JavaScript 对象甚至是二进制数据如图片、文件 Blob。这意味着你可以把图片、视频、文档等直接存到本地数据库中。3. 使用场景IndexedDB 并不是用来替代 LocalStorage 的它专治“大容量、复杂结构”的痛点使用场景说明典型示例离线应用PWA在断网时网页可以从 IndexedDB 中读取之前缓存的数据让你依然能查看和编辑。在线文档、离线地图、邮件客户端海量数据缓存把电商网站的超长商品列表、后台管理系统的字典数据存在本地能极大减少网络请求提升页面秒开速度。商品列表缓存、字典数据本地化本地业务数据前端表单的复杂草稿、本地编辑的视频/图片、不需要实时同步到后端的购物车数据。富文本编辑器草稿、图片编辑历史4. 怎么用举例⚠️新手高能预警IndexedDB 的原生 API 非常繁琐充满了各种事件回调onsuccess、onupgradeneeded等直接写原生代码极其痛苦。强烈建议在实际开发中使用成熟的第三方封装库。推荐使用目前最流行、最优雅的 Promise 封装库idb。第一步安装库npminstallidb第二步创建/打开数据库并定义表结构import{openDB}fromidb;asyncfunctioninitDB(){constdbawaitopenDB(MyNotesDB,1,{upgrade(db){// 如果 notes 这个表对象存储不存在就创建它if(!db.objectStoreNames.contains(notes)){// 创建一个表主键 id 会自动递增db.createObjectStore(notes,{keyPath:id,autoIncrement:true});}},});returndb;}第三步增删改查CRUDasyncfunctionmanageNotes(){constdbawaitinitDB();// 1. 增加数据读写事务awaitdb.put(notes,{title:学习 IndexedDB,content:今天搞懂了本地数据库});// 2. 查询所有数据只读事务constallNotesawaitdb.getAll(notes);console.log(所有的备忘录:,allNotes);// 3. 按主键查询单条数据constnoteawaitdb.get(notes,1);// 获取 id 为 1 的数据console.log(第一条备忘录:,note);// 4. 删除数据awaitdb.delete(notes,1);// 删除 id 为 1 的数据}manageNotes();提示idb库将 IndexedDB 原生的回调式 API 封装成了 Promise 风格配合async/await使用代码简洁明了强烈推荐用它替代原生 API。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价