阅读视图

发现新文章,点击刷新页面。

最全Scss语法,赶紧收藏起来吧

Sass/SCSS 的嵌套

作用

  • 减少重复:不再处处写 .card 前缀。
  • 表达结构:一眼看出父子/状态/修饰的关系。
  • 就地维护:媒体查询、状态样式和主体样式放在一起,改组件更集中。
  • BEM 更顺手:&__el&--mod&.is-xxx 自然生成。

下面我们来看下它嵌套编译的效果

.card {
  .title { color: #333; }        // 后代
  &:hover { box-shadow: ...; }   // 父的伪类
  .actions > .btn { ... }        // 组合器保留
}

编译后:

.card .title{color:#333}
.card:hover{box-shadow:...}
.card .actions > .btn{...}

变量与类型

$size: 16px;
$brand: #0b81ff;
$title: "Hello";
$on: true;
$none: null;

// 列表 & 映射
$spaces: 4px 8px 16px;
$palette: (primary: #0b81ff, success: #22c55e);
  • null 的属性会被跳过。
  • map.get($palette, primary) 取值。

选择器嵌套与父选择器 &

.card {
  padding: 16px;
  &--active { box-shadow: 0 4px 12px rgba(0,0,0,.08); } // BEM 修饰
  &:hover { transform: translateY(-1px); }               // 伪类
  .title { font-weight: 600; }                           // 子元素
}

嵌套尽量 ≤ 3 层,避免选择器过长。

插值 #{}(拼接变量)

$base: btn;
$radius: 8;
.#{$base} { border-radius: #{$radius}px; } // → .btn { border-radius: 8px }

Mixin / Include(可复用片段)

@mixin flex-center($gap: 8px) {
  display: flex; align-items: center; justify-content: center; gap: $gap;
}
.toolbar { @include flex-center(12px); }

// 带内容插槽
@mixin layer($z: 1) { position: relative; z-index: $z; @content; }
.badge { @include layer(10) { pointer-events: none; } }

Function(返回计算值)

@use "sass:math";
@function px2rem($px, $base: 16) { @return math.div($px, $base) * 1rem; }
.title { font-size: px2rem(20); }

Dart Sass 用 math.div 代替 / 除法。

控制指令

$theme: dark;

@if $theme == dark { body { background:#0f172a; color:#e2e8f0; } }
@else { body { background:#fff; color:#111; } }

@each $gap in (4px, 8px, 12px) { .gap-#{$gap} { gap: $gap; } }

@for $i from 1 through 3 { .col-#{$i} { width: (100%/3)*$i; } }

$i: 0;
@while $i < 3 { .ring-#{$i} { outline-width: $i+1px; } $i: $i + 1; }

占位选择器与继承(谨慎用)

%btn-base { font: inherit; padding: .5em 1em; border: 1px solid transparent; }
.primary { @extend %btn-base; background: #0b81ff; color: #fff; }

@extend 可能引发“选择器膨胀”,组件库更推荐用 mixin 复用样式。

模块化导入(现代写法)

/* tokens/_color.scss */
$primary: #0b81ff !default;
@mixin btn() { padding: 8px 12px; }

/* design/_index.scss —— 聚合出口 */
@forward "../tokens/color";

/* app.scss —— 使用与配置 */
@use "./design/index" as d with ($primary: #0052d9);
.button { color: d.$primary; @include d.btn(); }
  • @use:有命名空间、成员只读;用 with 配置带 !default 的变量。
  • @forward:做“总入口”(barrel),对外转发变量/函数/mixin。
  • 旧式 @import 已弃用;@import url(...)CSS 导入(不会带来 Sass 变量)。

调试与错误

@use "sass:meta";
@debug meta.type-of((a: b));
@warn "Deprecated var, will be removed.";
@error "Invalid token";

全局方法(旧写法)

颜色相关

写法 说明 示例/结果
darken(#fff, 10%) 调暗亮度 10%(不是透明度) #fff → #e6e6e6
lighten(#fff, 30%) 调亮亮度 30%(纯白几乎无变化;也不是透明度) #fff → #fff
hsl(0, 100%, 50%) 色相/饱和度/亮度 构造颜色(红) #ff0000
adjust-hue(#fff, 180deg) 色相旋转 180°(对白/灰无可见变化) #fff
saturate(#fff, 10%) 提高饱和度(对白/灰无效) #fff
desaturate(#fff, 10%) 降低饱和度 #fff
transparentize(#fff, 0.1) 增加透明度(α 减 0.1) rgba(255,255,255,0.9)
opacify(#fff, 0.1) 增加不透明度(α 加 0.1) rgba(255,255,255,1)

注:transparentize/opacify 的第二个参数是 0–1 的无单位数,不是百分比。

计算

写法 说明 示例/结果
abs(-10px) 绝对值 10px
ceil(-12.5px) 向上取整(朝 +∞) -12px
round(12.3px) 四舍五入 12px
floor(12.8px) 向下取整 12px
percentage(650px / 1000px) 比例转百分比 65%(因为 px/px → 无单位 0.65)
min(1, 2, 3) 取最小值(编译期) 1
max(1, 2, 3) 取最大值(编译期) 3

提醒:min/max 比较带单位时需同维度可换算(如 pxin);px vs rem/vw 等不同维度请用 CSSmin()/max()/clamp() 在浏览器端计算。

字符串相关

写法 说明 示例/结果
to-upper-case("hello") 转大写 "HELLO"
to-lower-case("HELLO") 转小写 "hello"
str-length("hello") 字符串长度 5
str-index("hello", "h") 返回首次出现位置(1 基;找不到为 null 1
str-insert("hello", "world", 5) 在索引处插入子串(1 基;负数为倒数) "hellworldo"

内置模块Api(新写法)

这里主要介绍 sass:color、sass:math、sass:string,其他有需要的可以自行去了解

sass:color(调色/透明度/混色)

@use "sass:color";
$brand: #1677ff;

/* 1) 按比例靠近极值(推荐,变化更自然) */
.bg-hover  { background: color.scale($brand, $lightness: 10%); }   // 变亮
.bg-active { background: color.scale($brand, $lightness: -12%); }  // 变暗
.bg-ghost  { background: color.scale($brand, $alpha: -30%); }      // 更透明

/* 2) 加/减固定量(严格步长) */
.more-sat  { background: color.adjust($brand, $saturation: 15%); }
.rotate    { background: color.adjust($brand, $hue: 30deg); }      // 旋转色相

/* 3) 设定绝对值(直接锁定到目标) */
.fixed-a   { background: color.change($brand, $alpha: 0.6); }      // α=0.6
.fixed-l   { background: color.change($brand, $lightness: 40%); }  // L=40%

/* 4) 混色(做浅/深阶) */
.light-1   { background: color.mix(#fff, $brand, 20%); }           // 更浅
.dark-1    { background: color.mix(#000, $brand, 15%); }           // 更深

/* 5) 反相(生成对比色) */
.invert    { color: color.invert($brand, 100%); }

小抄:

  • 变亮/变暗 → scale($lightness: ±x%);透明度 → scale($alpha: ±x%)
  • 固定步长 → adjust;锁定目标值 → change;两色过渡 → mix

sass:math(数值/单位运算)

@use "sass:math";

/* 1) 除法一定用 math.div */
.title { font-size: math.div(20, 16) * 1rem; }  // 1.25rem

/* 2) 取整/比较/钳制(编译期) */
.box  { margin: math.round(4.6px); }            // 5px
.maxw { max-width: math.min(960px, 1in); }      // 单位可换算才行
/* 混单位(px vs rem/vw)请用 CSS 的 min()/clamp() 在运行时算 */

/* 3) 常用函数 */
$pi: math.$pi;                 // 3.14159…
$len: math.hypot(3, 4);        // 5(向量长度)
$angle: math.atan2(1, -1);     // 135deg(可直接放到 rotate())

/* 4) 小工具:px→rem */
@function px2rem($px, $base: 16) { @return math.div($px, $base) * 1rem; }
.btn { padding: px2rem(10) px2rem(14); }

要点:

  • min/max/clamp 仅比较同维度可换算单位(px↔in 等);混单位用 CSSmin()/max()/clamp()
  • 乘除尽量“有单位 × 无单位”,避免产生非法复合单位。

sass:string(拼接/切片/大小写/引号)

@use "sass:string";

/* 1) 拼接请用插值 #{} */
$ns: "app"; $block: btn;
.selector { content: "#{$ns}-#{$block}"; }   // "app-btn"

/* 2) 长度/索引/切片/插入(索引从 1 开始,负数从尾部数) */
@debug string.length("hello");                    // 5
@debug string.index("btn--primary", "--");        // 4
@debug string.slice("abcdef", 2, 4);              // "bcd"
@debug string.insert("color", "-primary", 6);     // "color-primary"

/* 3) 大小写/引号 */
@debug string.to-upper-case("btn");               // "BTN"
@debug string.quote(btn);                         // "btn"
@debug string.unquote("bold");                    // bold(变标识符)

/* 4) 唯一 id(避免命名冲突) */
$uid: string.unique-id();                         // 比如 "u5ab9"
@keyframes fade-#{$uid} { from{opacity:0} to{opacity:1} }
.fade { animation: fade-#{$uid} .2s ease; }

小抄:

  • 串联字符串 → **插值 #{}**;结构化处理再用 length/index/slice/insert
  • quote/unquote 控制是否带引号;unique-id() 做不冲突的 keyframes/变量名。

Scss 的四种导入方式你都知道吗

想把样式拆模块、做主题、又不想全局变量乱飞?这篇把 SCSS 的四种“导入/组织”方式一次讲透:@use@forward、Sass 旧式 @import、以及 CSS 的 @import url(...)。含示例、对比表、迁移步骤与避坑清单。


@import "xxx":Sass 旧式导入(已弃用

作用
编译期把被导入的 SCSS 内容“直接拼接”到当前位置,变量/混入进入全局,容易重复加载、顺序踩坑。Dart Sass 已弃用,推荐使用@use/@forward

示例

@import "./tokens";   // 编译时把 tokens.scss 的内容贴进来(非 CSS 运行时)

@import url(...)CSS 的导入

作用
浏览器在运行时请求外部 CSS 文件;不会引入 Sass 的变量/混入/函数。

示例

@import url("/base.css");           /* 或者 @import "/print.css" print */

位置限制
必须在样式表最前面(在任何普通规则前;前面最多有 @charset / 其它 @import)。影响性能,一般不建议使用。


@use:模块化导入(推荐

作用
将另一个 SCSS 文件当作“模块”加载,默认通过命名空间访问变量/函数/混入;只加载一次、不污染全局,避免命名冲突。

示例

/* tokens/_color.scss */
$primary: #0b81ff !default;
@mixin btn { padding: 8px 12px; border-radius: 8px; }

/* app.scss */
@use "./tokens/color" as c; // 起别名 c

.button {
  color: c.$primary;
  @include c.btn();
}

配置默认变量(with + !default)

@use "./tokens/color" as c with (
  $primary: #0052d9   // 仅能配置标了 !default 的顶层公开变量
);

要点

  • 命名空间:@use "x" as x;不建议as *(去掉前缀,易冲突)。
  • 变量只读:c.$primary 不能在引入方直接赋值,只能用 with加载时配置。

@forward:聚合/转发导出

作用
把若干模块“转发”出去,做一个统一出口(barrel/index)。自己文件里不能直接使用转发来的成员;若要自用,再 @use 一次。

示例

/* design/_index.scss —— 聚合出口 */
@forward "../tokens/color" show $primary, btn;  // 只暴露想给外部用的成员
@forward "../tokens/spacing" as space-*;        // 导出时加前缀

/* app.scss —— 使用统一出口 */
@use "./design/index" as d;

.card {
  color: d.$primary;
  margin: d.$space-lg;
}

在出口处预配置主题(with)

/* design/_index.scss */
@forward "../tokens/color" with ($primary: #333);

注意:一个模块只能被配置一次。上游已通过 @forward ... with 配过,下游再配会报错。


五分钟速查表

能力/特性 @use @forward Sass @import "x" CSS @import url(...)
发生时机 编译期 编译期 编译期 运行时(浏览器请求)
命名空间 有(可 as * 去掉) 对外导出,不给本文件用 无(全局拼接)
去重加载 ❌ 可能重复 N/A(浏览器请求)
变量可配置 通过 with 配置 !default 通过 with 预配置并转发 通过“先定义后导入”覆盖(老做法)
全局污染
位置限制 任意 任意 任意(编译期) 顶部(在普通规则前)
适用场景 模块化使用 统一出口/SDK 旧项目遗留 引入纯 CSS(不推荐)

常见误区 & 快速排坑

  1. @import url('./variable.scss') 用后 $color 未定义

    • 这是 CSS 导入,不会带来 Sass 变量。
    • 用:@use "./variable" as v;color: v.$color;
  2. 我在文件里写 $primary: red,为啥 @use 来的变量没变?

    • @use 是模块隔离;你改的是当前文件$primary,不是模块里的。
    • 正确:@use "./tokens" with ($primary: red); 且源变量需 !default
  3. 去命名空间 as *@import 一样吗?

    • 不是。as * 只是省掉前缀,仍是模块系统(只加载一次、不污染全局)。
    • 有冲突风险:多个模块导出同名变量会报错。
  4. @forward 文件里用不了转发来的变量?

    • 对。@forward 只是出口;若当前文件要用,@use 一次
  5. 位置问题:为啥我的 @import 被忽略?

    • CSS 的 @import 必须在最顶部(普通规则之前)。Sass 旧式 @import 不限位置,但已经弃用。

迁移指北:从 @import@use/@forward

目录建议

styles/
  tokens/
    _color.scss     // 变量均加 !default
    _spacing.scss
  mixins/
    _typography.scss
  design/
    _index.scss     // 聚合出口(只 forward)
  app.scss          // 项目入口(只 use design)

改造步骤

  1. 给需要对外可配的变量都加 !default

  2. 建“总出口”:

    /* design/_index.scss */
    @forward "../tokens/color";
    @forward "../tokens/spacing";
    @forward "../mixins/typography";
    
  3. 入口使用:

    /* app.scss */
    @use "./design/index" as d with (
      $primary: #0052d9,
      $space-lg: 24px
    );
    
    .btn { color: d.$primary; margin: d.$space-lg; }
    
  4. 删除旧式 @import,逐步把业务文件改为 @use 指向 design/_index.scss


结语

  • 新项目:只用 @use + @forward
  • 做 SDK/设计系统:@forward 聚合成一个总入口,外部只 @use 它。
  • 变量可配:源头加 !default,引入处用 with
  • 避免 @import 与 CSS 的 @import url(...)(性能差、易踩坑)。

需要我按你的项目目录,直接给一份可跑的 @use/@forward 模板吗?把目录贴过来我就给你落地版。

JavaScript 严格模式

它是什么

给 JS 开个“较真模式”。一旦开启,JavaScript 会对一些原本“容忍/默默忽略”的危险写法直接抛错或改变行为,从而更早暴露 bug、更安全、更利于引擎优化。

为什么有意义

  • 更早发现错误:很多隐性 bug 当场报错,缩短排查时间。
  • 更安全:禁掉易被利用或难以理解的语法(如 witharguments.callee)。
  • 更一致:减少不同引擎下的歧义和历史包袱。
  • 更好优化:明确语义后,JS 引擎更容易做性能优化。

怎么开启

  • 在脚本或函数第一行写:"use strict";
  • ES6+ 模块与类默认就是严格模式;打包后的现代项目通常已经处于严格模式(多半不用手写这句)。

具体有什么变化(高频清单 + 例子)

  1. 禁止意外创建全局变量
"use strict";
function f() { x = 1 } // ReferenceError:x 未声明

(非严格下会悄悄变成 window.x

  1. 普通函数里的 this 不再自动指向全局
function f(){ return this }
f(); // 非严格: window/global;严格: undefined
  1. 参数名不能重复
function sum(a, a) {} // 严格: SyntaxError
  1. eval 变“隔离” (不再向外层注入变量)
"use strict";
eval("var x = 1;");
typeof x; // "undefined"
  1. 不能删除变量/函数/参数
"use strict";
var x = 1;
delete x; // SyntaxError
  1. 写入只读属性会抛错(非严格下静默失败)
"use strict";
const obj = {};
Object.defineProperty(obj, "id", { value: 1, writable: false });
obj.id = 2; // TypeError
  1. 禁用 with
"use strict";
with (obj) {} // SyntaxError
  1. 八进制字面量与转义受限
"use strict";
var n = 010; // SyntaxError(非严格下表示八进制 8)
  1. arguments 行为更“干净”
  • 不再和形参联动(改 arguments[0] 不会改到形参)。
  • 禁用 arguments.callee/arguments.caller(TypeError)。
  • 不能给 eval/arguments 赋值或作为变量名。
  1. 保留字更严格
    publicstatic(以及未来关键字)在严格模式下不可作为变量名。

什么时候我“已经在严格模式”?

  • 你用 ES modules (import/export)class:默认严格。
  • 现代打包工具(Webpack、Vite、Rollup)产物通常已严格。
  • 旧式 IIFE/脚本直连时,若没模块化,才可能需要手写 "use strict"

实战建议

  • 新项目/模块化项目:基本天然严格,无需额外处理。
  • 维护旧代码:可以按“文件或函数”为单位逐步加 "use strict",配合 eslint,把上面这些典型坑扫一遍。
  • 库作者:保持严格模式有助于用户环境一致性与可优化性。

一文教你搞懂sessionStorage、localStorage、cookie、indexedDB

引言

在前端开发中,浏览器提供了多种本地存储方式,例如 localStorage、sessionStorage 和 IndexedDB。它们能让我们把部分数据保存在用户浏览器端,从而减少与服务器的交互,提升应用的响应速度,并且在网络状况不佳时依旧能维持基本的使用体验。

然而,这些存储机制如果使用不当,会带来隐患:比如过度依赖 localStorage 存放敏感信息,可能引发安全问题;频繁无序地写入数据,会导致存储混乱甚至性能下降;或者没有清理策略,造成用户浏览器空间被大量占用。

相反,如果使用得当,则可以带来显著收益:页面打开更快、用户体验更流畅、离线状态下依旧可用,甚至还能实现类似“小型数据库”的功能,帮助前端在复杂应用中承担更多逻辑。

因此,用不好 → 安全隐患、数据不同步、性能下降;用好 → 性能优化、用户体验提升、支持离线能力。这正是前端工程师需要掌握这些浏览器存储技术的关键原因。

sessionStorage

什么是 sessionStorage

  • 浏览器提供的每个标签页独享的键值存储(同源内共享)。
  • 生命周期:创建 → 刷新/前进后退仍在 → 关闭该标签页或窗口即清空(或被代码/用户清除)。
  • 不随请求发送到服务器(和 Cookie 不同)。
  • 容量:约 ~5MB/源(不同浏览器略有差异)。

作用域与隔离

  • 同源 + 同标签页共享:同一标签页内的所有同源页面共享一个 sessionStorage。
  • 不同标签页彼此隔离;新开标签(即使同 URL)也有全新的 sessionStorage。
  • 通过 window.open() 打开的同源页,起始内容会被克隆一份,但之后互不影响。
  • storage 事件:sessionStorage 不会跨标签页广播变更(因为不共享)。在当前页变更也通常不会触发监听器去“同步别的页”。

适合

  • 表单临时状态(页面刷新不丢失)
  • 向导/多步流程的中间数据(每个标签页互不干扰)
  • 单次会话内的缓存(如本页拉过的列表、分页位置)

不适合

  • 任何敏感信息(明文可读,易被 XSS 窃取)
  • 需要跨标签页/长期持久的数据(用 localStorage/IndexedDB)
  • 大量/结构化数据(用 IndexedDB)

常见坑

  • 超配额会抛 QuotaExceededError:用 try/catch 捕获。
  • 类型丢失:没做 JSON 序列化会变成字符串 [object Object]
  • 同步 API:频繁大量写入会阻塞主线程;控制写入频率与体积。
  • Safari/移动端:隐私模式/内存压力下可能更严格,注意兜底(失败时退回内存变量)。

localStorage

什么是 localStorage

  • 浏览器提供的同源共享的键值对存储(Key-Value)。
  • 持久化:关闭页面/浏览器后仍保留,直到被代码或用户清除(或清站点数据)。
  • 仅前端可见:不会随请求自动发给服务器(和 Cookie 不同)。
  • 字符串存储:值一律按字符串保存(对象需 JSON 序列化)。

生命周期与作用域

  • 生命周期:创建 → 刷新/切页仍在 → 手动清除/清站点数据 才消失。
  • 作用域(Origin) :协议+域名+端口三者相同才共享。
  • 跨标签页:同源所有标签页/窗口共享同一份 localStorage。
  • 第三方/隐私机制:现代浏览器在第三方 iframe、隐私/无痕模式下可能更严格或不可用。

适合

  • 表单临时状态(页面刷新不丢失)
  • 向导/多步流程的中间数据(每个标签页互不干扰)
  • 单次会话内的缓存(如本页拉过的列表、分页位置)

不适合

  • 任何敏感信息(明文可读,易被 XSS 窃取)
  • 需要跨标签页/长期持久的数据(用 localStorage/IndexedDB)
  • 大量/结构化数据(用 IndexedDB)

与 localStorage / Cookie 的关键区别

  • 生命周期:sessionStorage 关标签就没;localStorage 长久保存;Cookie 取决于 Expires/Max-Age
  • 是否随请求发送:只有 Cookie 会自动带到请求里;sessionStorage/localStorage 都不会。
  • 作用域:sessionStorage 是每标签页独享;localStorage 是同源所有标签共享

常见坑

  • 超配额会抛 QuotaExceededError:用 try/catch 捕获。
  • 类型丢失:没做 JSON 序列化会变成字符串 [object Object]
  • 同步 API:频繁大量写入会阻塞主线程;控制写入频率与体积。
  • Safari/移动端:隐私模式/内存压力下可能更严格,注意兜底(失败时退回内存变量)。

cookie

什么是 Cookie

  • 一种由浏览器保存的 小块文本数据(通常不超过 4KB),由服务器或前端 JS 设置。
  • Cookie 会自动随同域请求被发送到服务器,用于身份识别、会话保持、用户偏好等。
  • 每个 Cookie 都包含 键值对、域、路径、过期时间、安全属性等信息。

生命周期

  • 会话 Cookie:不设置 ExpiresMax-Age,浏览器关闭即消失。
  • 持久 Cookie:设置了过期时间,在过期前都会保留,即使浏览器关闭。
  • 删除 Cookie:可设置一个过去的时间作为过期时间,或用户手动清除。

适合

  • Session ID、用户登录状态(结合 HttpOnly + Secure + SameSite)
  • 必须随请求发送的小块状态

不适合

  • 大量数据(容量小)
  • 敏感信息(若没设置 HttpOnly,容易被 JS 读出)
  • 高频更新数据(每次请求都会带上,浪费带宽)

Cookie 是小容量、随请求自动发送、适合会话管理的存储方式;现代前端更多使用 localStorage / sessionStorage / IndexedDB 存储本地数据,而 Cookie 专注于“和服务器同步状态”

IndexedDB

什么是 IndexedDB

  • 浏览器提供的 非关系型本地数据库(NoSQL),适合存储大量结构化数据。
  • 面向对象的存储方式:数据存放在「对象仓库(Object Store)」里,而不是表格行列。
  • 支持 索引、事务、游标,能实现接近数据库的查询与操作。
  • 容量:可达数百 MB(甚至更大,具体取决于浏览器和用户设置)。

生命周期与作用域

  • 生命周期:持久保存,除非被代码/用户/清站点数据手动删除。
  • 作用域:基于同源策略(协议+域名+端口一致才共享)。
  • 多标签页共享:同源下所有标签页可访问同一数据库。

特点

  • 异步 API(不会阻塞主线程,基于事件回调或 Promise)。
  • 事务:读写操作必须在事务中进行,保证原子性。
  • 索引:可为字段建立索引,加快查询。

适用场景

  • 离线应用:在无网络时保存数据,下次上线再同步。
  • 大体量数据缓存:如文章列表、聊天记录、本地音乐缓存。
  • 复杂数据结构:存储 JSON、二进制数据(如 Blob、文件)。
  • 渐进式 Web 应用(PWA) :结合 Service Worker 做完整离线方案。

对比

特点 Cookie localStorage sessionStorage IndexedDB
生命周期 可设置过期时间;默认会话级别,关闭浏览器清除 永久保存,除非手动清除 会话级:关闭标签页/窗口清除 永久保存,除非手动清除
容量限制 ~4KB 左右 ~5MB ~5MB 可达数百 MB(依浏览器而定)
作用域 同源,且可配置路径;会随请求发送到服务器 同源 同源(同窗口/标签页) 同源
是否随请求发送 ✅ 每次请求都会带上(增加带宽) ❌ 不会 ❌ 不会 ❌ 不会
可访问性 JS 可访问(除非设置 HttpOnly);服务器也可读写 仅 JS 端可访问 仅 JS 端可访问 仅 JS 端可访问
适合场景 存储少量需随请求携带的信息(如 Session ID) 保存长期数据(主题、配置、token 等) 保存临时数据(表单状态、页面刷新不丢失) 保存大量结构化数据(离线应用、本地数据库)
安全性 容易被窃取;应结合 HttpOnly + Secure + SameSite 明文存储,易被 XSS 窃取,不建议存敏感信息 明文存储,易被 XSS 窃取,不建议存敏感信息 相对安全,支持事务,但依旧避免存敏感数据

结论

技术 生命周期 容量 特点 适合使用场景
Cookie 可设过期时间;会话级或持久 ~4KB 每次请求都会自动携带到服务器;支持 HttpOnly / Secure / SameSite 安全属性 身份认证(Session ID、CSRF Token)、服务端需要的少量状态
sessionStorage 浏览器标签页/窗口关闭即清空 ~5MB 同源、同标签页共享;不随请求发送;仅 JS 端可见 临时存储:表单数据、页面状态(刷新不丢)、单次会话流程
localStorage 永久保存,除非手动清除 ~5–20MB 同源所有标签页共享;不随请求发送;同步 API,值为字符串 长期存储:主题、语言偏好、轻量缓存(如最近浏览)、跨标签页共享小数据
IndexedDB 永久保存,除非手动清除 可达数百 MB+ 面向对象数据库,支持事务、索引、异步 API;结构化存储 复杂/大量数据:离线应用、大体量缓存(文章、消息)、需要查询的结构化数据
❌