VS Code 这次稳了!CSS Anchor Positioning 彻底终结 WebView 定位卡顿

告别 getBoundingClientRect 引发的 Layout Thrashing,浏览器原生接管浮层定位,性能提升不止一个量级

话不多说,直接上结论:VS Code 1.119 把 WebView 的定位机制从 JS 手动计算切换成了 CSS Anchor Positioning。这波优化直接解决了多 WebView 场景下的卡顿和位置错位问题,谁用谁香 🚀


1. 为什么 VS Code 突然"抛弃"了 JS 定位?

1.1 VS Code 干了什么?

先看官方 Release Note 里的核心变化:

  • 以前 :JS + getBoundingClientRect() 手动计算每个 WebView 的位置,每次 resize、scroll、拆分编辑器时都重新计算。
  • 现在:CSS Anchor Positioning,浏览器原生处理元素跟随关系,不再需要 JS 读取布局。

简单翻译:以前是靠 JS 量尺寸、算坐标、手动设 top/left;现在是 CSS 里写一句"这个元素跟着那个锚点",浏览器自动搞定。

1.2 什么是 WebView?为什么它是性能热点?

WebView 在 VS Code 里随处可见:

  • 插件渲染的 Markdown 预览
  • 内置的浏览器预览
  • 终端里的图形化界面
  • Git Graph、数据库管理工具等复杂插件

一个复杂项目里,可能同时存在 几十个 WebView 。当你拖拽拆分编辑器、滚动代码、或者调整侧边栏宽度时,所有 WebView 都要重新定位。以前用 JS 逐个计算,卡顿感直接拉满

1.3 这一点一定要注意!(踩坑预警)

VS Code 本身基于 Electron,本质上是一个"浏览器里的应用"。Electron 的渲染进程里,频繁调用 getBoundingClientRect() 会触发强制同步布局(Forced Synchronous Layout),也就是 Layout Thrashing。

我之前在一个 Electron 项目中写过一个浮层工具提示,每次鼠标移动都重新计算位置,结果滚动时掉帧明显。排查了半天才发现是 读写布局交替 导致的。踩过的坑,能绕电脑桌三圈 😭


2. 传统浮层定位方案:成也 JS,败也 JS

2.1 "三步走"的老套路

几乎所有浮层定位(Tooltip、Popup、ContextMenu)都长这样:

javascript 复制代码
const btnRect = button.getBoundingClientRect()
popup.style.left = btnRect.left + 'px'
popup.style.top = btnRect.bottom + 'px'

看起来就三行代码,问题在哪?

浏览器不是傻子,但它会被你逼疯 。当你读取 getBoundingClientRect 时,浏览器必须先重新计算样式重新布局,才能给你准确的坐标。这种"读一次 → 强制计算一次 → 写样式 → 再读 → 再计算"的模式,就是 Layout Thrashing。

典型流程帮你画出来了:

text 复制代码
读取布局(getBoundingClientRect)
    ↓
浏览器强制 reflow(同步计算)
    ↓
JS 计算坐标
    ↓
写回样式(top/left)
    ↓
下一次读取又开始循环...

2.2 什么叫 Layout Thrashing?一个生活化类比

想象你在超市搬货:

  • 老方案:你每搬一箱可乐,都要先停下手中的活,拿尺子量一下货架高度,再把箱子放上去。搬 10 箱,量 10 次。
  • 新方案:货架高度是固定的,你提前量一次,后面直接搬,不用反复测量。

JS 每次调用 getBoundingClientRect 就像"量尺子",浏览器被迫暂停当前工作,计算完再继续。多个 WebView 同时量尺子,主线程直接卡死

2.3 为什么 VS Code 特别容易踩坑?

普通网页可能只有 1-2 个浮层,VS Code 的场景极其复杂:

  • 多视图拆分:你可以把编辑器拆成 3 列 2 行,每个窗格里的 WebView 都要独立定位。
  • 高频事件:滚动、拖拽拆分条、改变侧边栏宽度 → 这些事件每秒触发几十次。
  • Electron 本质仍是浏览器:它没有绕过浏览器的 reflow 机制,反而因为多进程架构,让布局计算更加昂贵。

VS Code 官方说:"Positioning the webview here was done using JS, which called getBoundingClientRect. This call ends up being relatively slow because it triggers browser style recalculations and relayouts."

一句话总结:不是 JS 慢,是强制 reflow 慢。


3. CSS Anchor Positioning:从"算坐标"到"声明关系"

3.1 核心思想(敲黑板)

text 复制代码
以前:JS 测量位置 → 手动设置 top/left
现在:CSS 声明"元素跟随谁" → 浏览器内部自动布局

就像你跟朋友说"我跟着你走",而不是每走一步都拿出尺子量你们之间的距离。 浏览器就是那个最懂布局的"路痴克星",你只要告诉它锚点是谁、贴在哪边,它自动搞定。

3.2 三个核心 API(记笔记)

css 复制代码
/* 1. 给锚点起个名字 */
.button {
  anchor-name: --my-btn;
}

/* 2. 让定位元素声明跟随哪个锚点 */
.tooltip {
  position-anchor: --my-btn;
  
  /* 3. 用 anchor() 函数获取锚点坐标 */
  left: anchor(right);
  top: anchor(top);
}
API 作用 类比
anchor-name 给元素起个锚点 ID 给地标贴个标签
position-anchor 告诉定位元素"你跟着谁" 你说"我跟着那个地标"
anchor() 获取锚点的某条边位置 问"它的右边在哪?"

3.3 浏览器到底做了什么?

以前你调用 getBoundingClientRect,浏览器被迫立刻计算布局。现在你用 CSS 声明关系,浏览器在 layout 阶段统一处理,不需要 JS 介入。

打个比方:

  • 旧方案:每个浮层都像"插队"的乘客,非要司机当场算座位。
  • 新方案:所有乘客提前说好"我跟谁坐一起",司机一次性安排完所有座位,高效不堵车。

这波优化,本质是把"运行时计算"变成了"编译时声明"。


4. 一个最小 Demo:对比新旧方案

4.1 JS 旧方案(又慢又容易出错)

html 复制代码
<div class="btn">悬浮我</div>
<div class="popup" style="display: none;">我是浮层</div>
javascript 复制代码
const btn = document.querySelector('.btn')
const popup = document.querySelector('.popup')

btn.addEventListener('mouseenter', () => {
  const rect = btn.getBoundingClientRect()
  popup.style.left = rect.left + 'px'
  popup.style.top = rect.bottom + 'px'
  popup.style.display = 'block'
})

问题清单:

  • 每次 hover 都触发一次强制 reflow
  • 滚动时如果没重新计算,浮层位置就错位
  • resize 时需要监听事件重新算
  • 多个浮层互相独立,无法保证同步

4.2 CSS 新方案(无 JS,自动跟随)

html 复制代码
<div class="btn">悬浮我</div>
<div class="popup">我是浮层</div>
css 复制代码
.btn {
  anchor-name: --btn-anchor;
}

.popup {
  position-anchor: --btn-anchor;
  left: anchor(right);
  top: anchor(top);
  /* 默认隐藏,hover 时显示 */
  display: none;
}

.btn:hover + .popup {
  display: block;
}

亲测有效,可直接套用: 不需要监听任何事件,不需要 getBoundingClientRect,滚动、resize 全部自动同步。

4.3 进阶 Demo:hover 切换锚点

这个例子直接封神------一个元素可以动态跟随不同锚点

html 复制代码
<div class="btn-a">按钮A</div>
<div class="btn-b">按钮B</div>
<div class="anchor">被定位元素C</div>
css 复制代码
.btn-a {
  anchor-name: --anchor-a;
}
.btn-b {
  anchor-name: --anchor-b;
}
.anchor {
  position-anchor: --anchor-a;
  left: anchor(center);
  top: anchor(bottom);
}
/* 当 hover B 时,动态切换锚点 */
body:has(.btn-b:hover) .anchor {
  position-anchor: --anchor-b;
}

演示效果:hover A 时,C 贴在 A 下方;hover B 时,C 瞬间移动到 B 下方。这在前端要手动实现,得写一堆事件监听和位置计算,现在一行 CSS 搞定。


5. 为什么 CSS Anchor Positioning 可能改变前端生态?

5.1 过去:Popper.js / Floating UI 统治浮层定位

几乎每个项目里,只要涉及到 Tooltip、下拉菜单、气泡卡片,你都会看到:

javascript 复制代码
import { createPopper } from '@popperjs/core'
const popper = createPopper(reference, popper, options)

这些库内部干了什么?本质还是 getBoundingClientRect + 滚动监听 + resize 监听 + 手动设置 transform。功能强大,但代价是运行时开销和复杂的事件管理。

5.2 未来:浏览器原生接管

当 CSS Anchor Positioning 普及后:

css 复制代码
.tooltip {
  position-anchor: --target;
  left: anchor(right);
  top: anchor(center);
}

不需要引入任何第三方库,不需要写 JS,性能和稳定性直接拉满。

记住:工具的核心是简化工作,而非增加学习成本。浏览器原生能做的事,就别再引入一个 20KB 的库了。


6. 兼容性与现实情况(避坑指南)

6.1 当前支持情况

浏览器 最低版本 状态
Chrome 125+ ✅ 完全支持
Edge 125+ ✅ 完全支持
Electron 35+ ✅ 完全支持
Safari 16.4+ ⚠️ 部分支持(anchor-name 可用,但 anchor() 函数有限)
Firefox 129+ 🧪 实验性(需开启 layout.css.anchor-positioning.enabled)

6.2 什么场景适合直接用?

推荐场景:

  • Electron 桌面应用(VS Code 本身就是)
  • 内部管理后台(用户统一用 Chrome/Edge)
  • 移动端 WebView(Chrome 内核)

谨慎场景:

  • 面向大众的官网(Firefox 用户会看到布局错乱)
  • 强依赖 Safari 的项目(目前支持不完整)

6.3 如何做 fallback?(渐进增强)

css 复制代码
/* 支持新特性的浏览器 */
@supports (position-anchor: --anchor) {
  .popup {
    position-anchor: --btn;
    left: anchor(right);
  }
}

/* 不支持的浏览器回退到 JS 方案 */
@supports not (position-anchor: --anchor) {
  .popup {
    /* 通过 JS 动态设置 left/top */
  }
}

这一点一定要注意! 不要直接删掉旧的 JS 定位代码,先用 @supports 做特性检测,兼容老浏览器。


7. 总结

核心一句话

text 复制代码
CSS Anchor Positioning 的本质,
是把"浮层坐标计算"
变成"元素关系声明"。

技术价值

  • 性能提升:消除 Layout Thrashing,reduce 主线程压力
  • 代码简化:从几十行 JS 变成几行 CSS
  • 稳定性:不再有 resize/scroll 不同步导致的错位 bug

开发者启示

前后端生态的演进方向越来越清晰:浏览器正在逐步接管过去需要 JS 完成的 UI 逻辑。从 CSS Grid、Flexbox 到 Container Queries,再到现在的 Anchor Positioning,CSS 已经强大到可以替代大量布局类 JS 库。

技术的本质是解决问题,选择合适的工具,才能让自己从重复劳动中解放出来。

相关推荐
MonkeyKing71552 小时前
Flutter状态管理实战:全局、局部、页面状态拆分指南
前端·flutter
Panzer_Jack2 小时前
Copiwaifu:一个和 Claude Code、Codex、Copilot 等 AI 编程工具联动的 Live2D 桌宠[特殊字符]
前端·人工智能·copilot·web·live2d·pixi.js·easy-live2d
开源情报局2 小时前
从小红书评论区挖需求:我准备用 opencode 写一个 Chrome 插件
前端·javascript·chrome
用户125758524362 小时前
XYGo Admin 三级权限体系:RBAC 动态路由 + v-auth 按钮控制 + 字段级过滤全解析
前端
小李子呢02113 小时前
前端八股JS---Map / Set / WeakMap / WeakSet
开发语言·前端·javascript
冴羽3 小时前
3 招让你的 Shadcn 出海应用性能提升 40 倍
前端·javascript·next.js
中议视控3 小时前
网络中控系统通过推流软件实现可视化:RTSP,H265,WEB等推流
前端·网络
Hsuna3 小时前
Tailwind CSS 比起传统CSS框架无法实现的一些功能
前端·react.js