Day 20 _ 3D 翻卡_perspective 写错人,卡片就穿帮

Day 20 | 3D 翻卡:perspective 写错人,卡片就穿帮

本文是《30天精通CSS》专栏第 20 篇,订阅专栏不迷路。
摘要 :本文基于 CSS Transforms Level 2 与 CSS Transitions Level 1 现行规范,做一张正面问、背面答的 3D 翻卡。核心只有一句话:perspective 加在父级和加在被旋转元素自身,是两种完全不同的视点------前者让正反两面共享同一个消失点、整体翻转,后者让每个元素各转各的、3D 感明显变平。今天用两张对照图、一段翻卡 GIF、一条标了 backface 临界点的定格胶片把这件事讲透,再补 transform-style: preserve-3dbackface-visibility: hidden 这两个开关。适合已经会 transformtransition、准备进入真 3D 的读者,读完你能独立排查"翻一半镜像""卡片凭空消失"这两类经典事故。

本文目录

  • 卡片没翻过去,是先"没了"

  • perspective 写父级 vs 写自身:一眼见高下

  • 拆开看:为什么视点必须挂在父级

  • 三层套娃:preserve-3d 与 backface 临界点

  • perspective 取值:鱼眼与压平之间

  • 最容易穿帮:漏一行 preserve-3d,卡片消失

  • 今晚 20 分钟:把三个开关各拨错一次

  • 明天开始发剧本,翻卡这块先留个尾巴

卡片没翻过去,是先"没了"

昨晚有读者在评论区发来一段动图:正面题目、背面答案,结构摆得挺清楚,可鼠标一悬停,卡片不是翻过去------是整块凭空消失,咣,啥也不剩;移开再移回来,它又"啪"地弹出来了。浏览器呢,照例一声不吭。这套"不报错但没生效"的路数你八成眼熟:Day 17 里那个过渡不动的 display,和 Day 19 里那个"拿 margin 做动画、抖的是整页",病根全在动错了东西。今天这位穿帮是第三个兄弟:3D 上下文压根没建起来,backface-visibility 就老老实实把正反两面全藏了。

先把目标立住:一张卡片,正面是题目,悬停时绕 Y 轴翻过去,露出背面的答案。这是 3D 动效里最有仪式感、也最容易穿帮的一种。上一篇 Day 19 里 rotate() 还在纸面上转圈,今天升级到 rotateY(),把卡片立起来绕垂直轴转,并把三种"看着对、其实错"的写法一次排干净。标题里的 perspective 是全场主角------它决定你的"眼睛"离屏幕多远。

perspective 写父级 vs 写自身:一眼见高下

同样一句 rotateY(180deg),透视的落点不同,味道完全不同。下面两张卡是当天最该记住的对照。第一张:透视写在父容器上。

Plaintext 复制代码
<!-- 《30天精通CSS》Day 20 · 平头哥 · 技术改变世界,勤奋改变人生 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>写法A:透视写在父级</title>
  <style>
    * { box-sizing: border-box; }
    body { max-width: 720px; margin: 30px auto; padding: 0 16px;
      font-family: "Microsoft YaHei", sans-serif; background: #f6f4ee; color: #24292f; }
    /* 透视挂在最外层:所有 3D 子元素共享同一个消失点,整体翻转 */
    .scene { perspective: 800px; width: 260px; margin: 24px auto; cursor: pointer; }
    .card { position: relative; width: 100%; height: 160px;
      transform-style: preserve-3d; transition: transform .6s cubic-bezier(.4,0,.2,1); }
    .scene:hover .card { transform: rotateY(180deg); }
    /* 正反两面叠在同一格,背面预先转好 180 度 */
    .face { position: absolute; inset: 0; border-radius: 12px; backface-visibility: hidden;
      display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 15px; }
    .front { background: linear-gradient(135deg,#2563eb,#7c3aed); color: #fff; }
    .front small { opacity: .8; font-size: 12px; margin-top: 6px; }
    .back { background: #2f8f4e; color: #fff; transform: rotateY(180deg); text-align: center; padding: 0 18px; }
    .cap { text-align: center; font-size: 13px; color: #1B4055; margin-top: 8px; }
  </style>
</head>
<body>
  <div class="scene">
    <div class="card">
      <div class="face front">什么是盒模型?<small>悬停看答案</small></div>
      <div class="face back">content、padding、border、margin 四层</div>
    </div>
  </div>
  <p class="cap">写法 A:perspective 在父级 ------ 正反两面像一叠卡一起绕轴翻,纵深真实</p>
</body>
</html>

第二张:把透视塞进元素自己的 transform 里

Plaintext 复制代码
<!-- 《30天精通CSS》Day 20 · 平头哥 · 技术改变世界,勤奋改变人生 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>写法B:透视塞进自身 transform</title>
  <style>
    * { box-sizing: border-box; }
    body { max-width: 720px; margin: 30px auto; padding: 0 16px;
      font-family: "Microsoft YaHei", sans-serif; background: #f6f4ee; color: #24292f; }
    /* 透视写成 transform 函数:视点只作用于这一个元素,各转各的 */
    .card { position: relative; width: 260px; height: 160px; margin: 24px auto; cursor: pointer;
      transform-style: preserve-3d; transition: transform .6s; }
    .card:hover { transform: perspective(800px) rotateY(180deg); }
    /* 单元素自转:翻转轴心贴着元素,边缘透视偏弱,像平面翻折 */
    .face { position: absolute; inset: 0; border-radius: 12px; backface-visibility: hidden;
      display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 15px; }
    .front { background: linear-gradient(135deg,#2563eb,#7c3aed); color: #fff; }
    .front small { opacity: .8; font-size: 12px; margin-top: 6px; }
    .back { background: #2f8f4e; color: #fff; transform: rotateY(180deg); text-align: center; padding: 0 18px; }
    .cap { text-align: center; font-size: 13px; color: #6E3F12; margin-top: 8px; }
  </style>
</head>
<body>
  <div class="card">
    <div class="face front">什么是盒模型?<small>悬停看答案</small></div>
    <div class="face back">content、padding、border、margin 四层</div>
  </div>
  <p class="cap">写法 B:perspective 在自身 transform ------ 能翻,但绕近处的轴,透视感明显偏平</p>
</body>
</html>

两张图都是同一次悬停翻转,现象一比对就清楚:A 的近大远小更顺、像是从桌上把整张卡挑起来;B 翻转幅度一样,但边缘几乎没有透视拉伸,更像"平面翻折"。这不是 bug,是视点的归属不同------下面拆开讲机制。

维度 写法 A:透视在父级 写法 B:透视塞进自身 transform
视点归属 一组子元素共享一个消失点 每个元素各看各的
立体观感 近大远小明显,像整体抬起 偏平,像绕贴身轴自转
典型场景 正反两面协同翻的卡片、多卡同场 3D 单个元素独立自转的徽标
组合性 多个 3D 子元素能保持同一空间关系 只管自己,和邻居无统一透视
默认推荐 翻卡选它 单玩自转时才用

之所以 A 更"立体",是因为透视本质是一条投影链:所有子元素相对同一个视点计算近大远小,它们的边在翻转中会按同一套消失点收敛,视觉就统一。B 的 perspective() 函数只把投影套在这一个盒子身上,盒子外的邻居拿不到这个视点,于是翻转幅度虽然相同,却少了"整个舞台一起转"的一致性。我个人第一次做翻卡就是用了 B,感觉"哪不对"却说不上来,换成 A 立刻顺眼------差别就在这条共享的投影链上。

拆开看:为什么视点必须挂在父级

perspective 是"眼睛到屏幕的距离"

perspective: 800px 读作:你的眼睛离屏幕 800 像素。值越小,眼睛凑得越近,透视越夸张;越大越远,画面越平。它本身不产生 3D,只是给 3D 变换提供一套投影规则------真正把子元素留在三维空间里的是 transform-style: preserve-3d。所以翻卡穿帮先别怪透视,查一查是谁没把子元素留在 3D 里嘛。

父级写法为什么更适合翻卡

翻卡要翻的是"整个卡片舞台",正反两面是同一空间里的两个平面 。透视写在父级时,这一组平面共享同一个消失点,翻转时近边变大、远边变小,才像从桌上挑起来。把透视塞进元素自身(transform: perspective() rotateY()),视点只服务这一个盒子,它更像"绕贴身的轴自转",多个面之间不再有统一的透视关系,所以 B 看起来偏平。

这里没有谁对谁错,只有适用场景 :单元素独立自转(比如一张卡自己玩),B 够用;正反两面协同翻转、或一组元素同场 3D,A 才是默认选择。这个机制从 CSS Transforms Level 2 定稿起没变过,属于长期有效的基础。顺带更新一句时效信息:早年的 -webkit-perspective 带前缀写法如今已废弃,直接写 perspective 即可;更早期用 filter 拼凑的伪 3D 方案也已被真三维 transform 取代,新代码不必再碰。

三层套娃:preserve-3d 与 backface 临界点

正确翻卡是三层结构:外层管透视,中层管翻转,内层是正反两面。缺任何一层都会穿帮。这三层的顺序不是随便排的:透视必须在最外层,是因为它要为"里面所有 3D 变化"提供一个统一的舞台坐标;翻转放在中层,是因为它既是被透视作用的对象,又是正反两面的父级;两面贴在最里层靠 absolute 叠在同一格,才能翻过去时精确对齐。我第一次没搭这三层,把 perspectiverotateY 写在了同一个 .card 上,结果正反面各翻各的、根本对不齐,排查了半小时才想明白"少了一层舞台"。下面是把三层补齐、又各自职责单一的完整写法,注意每一层的注释。

Plaintext 复制代码
<!-- 《30天精通CSS》Day 20 · 平头哥 · 技术改变世界,勤奋改变人生 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>三层结构逐帧</title>
  <style>
    * { box-sizing: border-box; }
    body { max-width: 900px; margin: 24px auto; padding: 0 16px;
      font-family: "Microsoft YaHei", sans-serif; background: #f6f4ee; color: #24292f; }
    .grid { display: flex; gap: 18px; justify-content: center; }
    .scene { perspective: 800px; width: 150px; height: 120px; }
    /* 第 2 层:翻转舞台,自己跑一段 rotateY 动画,好定格到每个角度 */
    .card { position: relative; width: 100%; height: 100%;
      transform-style: preserve-3d; animation: spin 6s cubic-bezier(.4,0,.2,1) infinite; }
    /* 第 3 层:两面叠同一格,背对观众自动隐藏 */
    .face { position: absolute; inset: 0; border-radius: 10px; backface-visibility: hidden;
      display: flex; align-items: center; justify-content: center; font-size: 13px; color: #fff; }
    .front { background: #2563eb; }
    .back { background: #2f8f4e; transform: rotateY(180deg); }
    @keyframes spin { 0% { transform: rotateY(0); } 50% { transform: rotateY(180deg); }
      100% { transform: rotateY(360deg); } }
    .lab { text-align: center; font-size: 12px; color: #8a8a86; margin-top: 6px; }
  </style>
</head>
<body>
  <div class="grid">
    <div><div class="scene"><div class="card"><div class="face front">正面</div><div class="face back">背面</div></div></div><div class="lab">0 度</div></div>
    <div><div class="scene"><div class="card" style="animation-delay:-0.5s"><div class="face front">正面</div><div class="face back">背面</div></div></div><div class="lab">转一点</div></div>
    <div><div class="scene"><div class="card" style="animation-delay:-0.75s"><div class="face front">正面</div><div class="face back">背面</div></div></div><div class="lab">接近 90 度</div></div>
    <div><div class="scene"><div class="card" style="animation-delay:-1s"><div class="face front">正面</div><div class="face back">背面</div></div></div><div class="lab">180 度:背面现身</div></div>
  </div>
</body>
</html>

下面这张定格胶片把同一段翻转动画钉在四个进度点上,专门标出 backface 的临界角度:大约 90 度那一瞬,正反两面同时侧对观众,卡片视觉上"消失"一下,再转到 180 度背面才完整现身。这帧"空"不是 bug,是 3D 的物理事实。

Plaintext 复制代码
# @frames 4
# @target .card
<!-- 《30天精通CSS》Day 20 · 平头哥 · 技术改变世界,勤奋改变人生 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>backface 临界点定格</title>
  <style>
    * { box-sizing: border-box; }
    body { max-width: 900px; margin: 30px auto; padding: 0 16px;
      font-family: "Microsoft YaHei", sans-serif; background: #f6f4ee; color: #24292f; text-align: center; }
    /* 单一翻转层:跑一段 0 到 180 的动画,胶片按进度定格 */
    .scene { perspective: 800px; width: 220px; height: 140px; margin: 10px auto; }
    .card { position: relative; width: 100%; height: 100%;
      transform-style: preserve-3d; animation: flip 4s linear 1 forwards; }
    /* 背对观众就隐藏,这是不穿帮的关键开关 */
    .face { position: absolute; inset: 0; border-radius: 12px; backface-visibility: hidden;
      display: flex; align-items: center; justify-content: center; font-size: 15px; color: #fff; }
    .front { background: linear-gradient(135deg,#2563eb,#7c3aed); }
    .back { background: #2f8f4e; transform: rotateY(180deg); }
    @keyframes flip { from { transform: rotateY(0); } to { transform: rotateY(180deg); } }
    .note { font-size: 13px; color: #8a8a86; }
  </style>
</head>
<body>
  <div class="scene"><div class="card">
    <div class="face front">正面:题目</div>
    <div class="face back">背面:答案</div>
  </div></div>
  <p class="note">定格点约 0% / 33% / 66% / 100%,对应 0° / 60° / 120° / 180°,90° 附近两面同时侧对观众</p>
</body>
</html>

四格现象逐格可读:第一格正面完整;第二格正面收窄、背面还没露;第三格(过了 90°)背面从窄变宽、正面趋零;第四格背面 180° 完整呈现、正面被 backface 藏净。这正是"对比数据"------把看不见的中间态钉死,读者一眼就能对上"哪一格该看到谁"。

一段 GIF 看翻卡的纵深感

胶片看的是"停在哪个角",GIF 看的是"动起来顺不顺"。下面这段是同一张卡连续循环翻转,透视在父级,你能直观感到 A 写法那种"从桌上挑起来"的纵深。

Plaintext 复制代码
# @frames 18
# @size 560x300
<!-- 《30天精通CSS》Day 20 · 平头哥 · 技术改变世界,勤奋改变人生 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>翻卡 GIF</title>
  <style>
    * { box-sizing: border-box; }
    body { margin: 0; height: 300px; display: flex; align-items: center; justify-content: center;
      background: radial-gradient(circle at 50% 40%, #ffffff, #eceadf); font-family: "Microsoft YaHei", sans-serif; }
    /* 透视在父级:正反两面共享消失点,循环翻转展示 3D 感 */
    .scene { perspective: 900px; width: 220px; height: 140px; }
    .card { position: relative; width: 100%; height: 100%;
      transform-style: preserve-3d; animation: flip 2.4s cubic-bezier(.45,.05,.35,1) infinite; }
    /* 两面靠 backface-visibility 各藏各的,永不穿帮 */
    .face { position: absolute; inset: 0; border-radius: 14px; backface-visibility: hidden;
      display: flex; flex-direction: column; align-items: center; justify-content: center; color: #fff; box-shadow: 0 12px 30px rgba(31,45,61,.25); }
    .front { background: linear-gradient(135deg,#2563eb,#7c3aed); font-size: 15px; }
    .back { background: #2f8f4e; transform: rotateY(180deg); font-size: 13px; text-align: center; padding: 0 16px; }
    @keyframes flip { 0% { transform: rotateY(0); } 45%,55% { transform: rotateY(180deg); } 100% { transform: rotateY(360deg); } }
  </style>
</head>
<body>
  <div class="scene"><div class="card">
    <div class="face front">正面</div>
    <div class="face back">背面:答案在这里</div>
  </div></div>
</body>
</html>

这段 GIF 每帧已烘入水印,进度取自 currentTime 的定值,所以每次生成的字节完全一致(可复现)。如果你本地看着"翻得发飘",多半是 transition 时长给太短、没在 90° 那一瞬留够时间------"唰"一下就翻过去了,来不及看清,而不是结构错。哈,先别急着怀疑人生。

perspective 取值:鱼眼与压平之间

透视值本身也是个可调参数。把三张卡的 perspective 分别设成 200px / 800px / 3000px,同时停在 55 度,对比最直观。

Plaintext 复制代码
<!-- 《30天精通CSS》Day 20 · 平头哥 · 技术改变世界,勤奋改变人生 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>perspective 取值对照</title>
  <style>
    * { box-sizing: border-box; }
    body { max-width: 900px; margin: 30px auto; padding: 0 16px;
      font-family: "Microsoft YaHei", sans-serif; background: #f6f4ee; color: #24292f; }
    .row { display: flex; gap: 20px; justify-content: center; }
    /* 三档透视:小=鱼眼,大=压平,中间=自然,停在同一角度比形变 */
    .scene { perspective: 200px; width: 150px; height: 110px; }
    .s2 { perspective: 800px; }
    .s3 { perspective: 3000px; }
    .card { position: relative; width: 100%; height: 100%;
      transform-style: preserve-3d; transform: rotateY(55deg); }
    .face { position: absolute; inset: 0; border-radius: 10px; backface-visibility: hidden;
      display: flex; align-items: center; justify-content: center; color: #fff; font-size: 14px; }
    .front { background: #2563eb; }
    .back { background: #2f8f4e; transform: rotateY(180deg); }
    .cap { text-align: center; font-size: 12px; color: #6E3F12; margin-top: 8px; }
  </style>
</head>
<body>
  <div class="row">
    <div><div class="scene"><div class="card"><div class="face front">200</div></div></div><div class="cap">200px:近、夸张</div></div>
    <div><div class="scene s2"><div class="card"><div class="face front">800</div></div></div><div class="cap">800px:自然</div></div>
    <div><div class="scene s3"><div class="card"><div class="face front">3000</div></div></div><div class="cap">3000px:几乎平</div></div>
  </div>
</body>
</html>

运行结果符合预期:200px 那张远端明显缩、近端鼓,像鱼眼;3000px 那张已接近纯平面旋转,几无纵深感。老实说,这组对照我第一次拼出来愣了一下------同一个 55 度,视觉上居然是三张不同的卡。注意适用边界:多数 UI 翻卡取 600--1200px 最耐看,太小会让卡片四角歪斜到出画,盯久了眼睛都累呀;太大则退化回 2D,白搭一次 3D。

还有一个常被忽略的孪生属性 perspective-originperspective 定"眼睛离屏幕多远",perspective-origin 定"眼睛正对着画面哪个点"(默认是中心 50% 50%)。把它挪到 left top,消失点就跑向左上角,卡片翻起来会有一种"从斜上方俯视"的透视倾斜;想模拟侧面偷看的错位感,就用它。日常翻卡保持默认中心最稳,但在做立体书脊、倾斜展台这类效果时,调一下 perspective-origin 往往比改 perspective 数值更能带来"活"的味道------它和 perspective 一样,都只认父容器上那一个,写进 transform 是不生效的。

最容易穿帮:漏一行 preserve-3d,卡片消失

三个开关里最阴的是中间那个。很多人照抄结构,唯独漏了 transform-style: preserve-3d,浏览器一声不吭,现象却离谱:一悬停,咔,卡片直接"没了"。

Plaintext 复制代码
<!-- 《30天精通CSS》Day 20 · 平头哥 · 技术改变世界,勤奋改变人生 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>漏了 preserve-3d</title>
  <style>
    * { box-sizing: border-box; }
    body { max-width: 720px; margin: 30px auto; padding: 0 16px;
      font-family: "Microsoft YaHei", sans-serif; background: #f6f4ee; color: #24292f; }
    .bar { background: #fff3e0; border: 1px solid #e3e0d8; border-radius: 6px;
      padding: 8px 14px; font-size: 14px; color: #8a3a28; margin-bottom: 22px; }
    .scene { perspective: 800px; width: 220px; margin: 0 auto; }
    /* 和正确版只少一行 transform-style: preserve-3d ------ 危险就藏在这里 */
    .card { position: relative; width: 100%; height: 150px; transition: transform .6s; }
    .scene:hover .card { transform: rotateY(180deg); }
    /* 没有 3D 空间,两面被压进同一平面,backface 把正反面全藏了 */
    .face { position: absolute; inset: 0; border-radius: 12px; backface-visibility: hidden;
      display: flex; align-items: center; justify-content: center; color: #fff; font-size: 15px; }
    .front { background: #2563eb; }
    .back { background: #2f8f4e; transform: rotateY(180deg); }
    .note { font-size: 13px; color: #8a3a28; margin-top: 16px; text-align: center; }
  </style>
</head>
<body>
  <div class="bar">悬停看:卡片不是翻过去,而是整块"消失"</div>
  <div class="scene"><div class="card">
    <div class="face front">正面</div>
    <div class="face back">背面</div>
  </div></div>
  <p class="note">排查动作:右键 → 检查 → 看 .card 的 Computed 里 transform-style 是不是 flat。漏了它,两面被压平,谁也不露脸。</p>
</body>
</html>

诊断链很清楚:没有 preserve-3d,浏览器把子元素"压平"到父平面(flat),于是正反两面同时背对或侧对观众,backface-visibility: hidden 忠实地把两面都藏了------现象就是卡片凭空消失。补回那一行即可。

今晚 20 分钟:把三个开关各拨错一次

小结

回头看今天这条因果链:perspective 给投影规则、preserve-3d 把结构撑进第三维、backface-visibility 让正反面各藏各的------三句话解释清楚了翻卡,也解释了三种穿帮。视点归谁(A vs B)不是对错,是场景;取值(200 / 800 / 3000)是口味,600--1200 最耐看。别背语法,就记这三个开关吧,拨错哪个,画面就坏在哪。

两道思考题

其一,如果你把 backface-visibility: hidden 只加在 .front 上、.back 不隐藏,翻转后你会看到什么?提示:想清楚"背对观众时不隐藏"意味着这一面的镜像会被谁看见。其二,把 perspective 同时写在 .scene.cardtransform 上会发生什么?是叠加还是各管一层?动手改一遍再回来看 3D 上下文的规则,比死记更牢。

今晚的活儿

我写的这套三层模板,你直接拷进 D:\我的网页\,今晚 20 分钟干三件事:① 把 perspective: 800px 改成 200px 与 3000px 各截一张,验证鱼眼与压平;② 删掉 transform-style: preserve-3d 复现一次"消失"------对,故意让它消失,看清楚病长什么样;③ 把 :hover 换成 :focus-withintabindex="0",让键盘也能翻卡,顺带把可访问性补上。

明天开始发剧本,翻卡这块先留个尾巴

今天的翻卡,背后跑的还是 Day 17 那条 transition:你悬停,它才补一段;手一移开,它倒着补回来。可有些动效等不了用户动手------加载圈得自己转,入场序列得按点排队。明天 Day 21 就讲这个:@keyframes 写剧本,animation 当播放器,从第一帧一路演到收工帧,还能无限循环。今晚先把翻卡翻顺,别急着贪新语法,哎,贪多了容易噎着。

参考资料


相关推荐
小聪7081 小时前
elpis-core 前端 Webpack 工程化实践
前端
2601_953988071 小时前
Ricon组态实时监控 - 毫秒级数据可视化
前端·物联网·数学建模·信息可视化·架构·前端框架
DisonTangor1 小时前
Qwen-Drive-1.0:首个统一 3D 感知、视觉问答与运动规划的自动驾驶视觉语言基础模型
人工智能·3d·自动驾驶
V158897262012 小时前
从滴滴模式看机器人租赁:撮合型租赁平台开发源码的调度系统设计思路
linux·前端·机器人
szarron2 小时前
国产手持式频谱分析仪选型攻略:TFN RC系列 vs HTOOL SA8T频谱分析仪 专业参数对比(军工/路测/调试全覆盖)
开发语言·前端·状态模式
开开心心就好2 小时前
免费桌签打印工具,支持批量导入名字
前端·javascript·人工智能·docker·jupyter·智能手机·语音识别
涛涛ing2 小时前
2026 年 9 月,整个 npm 生态的「心脏」都被 Rust 换了
前端
IT_陈寒2 小时前
明明设了默认值,为什么我的JavaScript函数参数还是undefined?
前端·人工智能·后端
CappuccinoRose2 小时前
Web Components 基础
前端·javascript·交互·web component·shadow dom