发布于

2026-第三十七周

作者

该周报主要为各个地方内容的汇总整理

技术

Half Past Fetch

这篇文章的核心是:await fetch(url) 只等到 HTTP 响应头,而不是整个响应。promise 在最后一个响应头字节到达时即刻 settle,此时 body 仍是一个正在接收数据的活跃 ReadableStream。这个看似微小的间隙带来一连串后果——连接被占用、clone() 只是对活跃流做 tee、abort 在 promise resolve 后仍然生效、基于 race 的超时只覆盖头部阶段——文章用 Node 与浏览器实验逐一验证,并指出正是这个特性让流式进度等能力成为可能。

  • 🔍 await fetch 只等到了一半:promise 在响应头完成时 settle,body 仍在传输,所以通常需要 await 两次
  • 📜 规范层面:HTTP-network fetch 在最后一个 header 字节处退出循环,挂上新的 ReadableStream;processResponse 被排为任务,规范明确注明 body 的流此后仍在被 enqueue
  • 🧪 实测差距:httpbin drip 示例中 headers 在 250ms 可读,body 在 3445ms 才结束;普通端点上两者只差不到 1ms,因此极易被忽视
  • 🚫 例外情况:HEAD 请求与 null body 状态码(101、103、204、205、304)的 body 为 null,没有后续传输
  • 🔌 连接仍归你所有:状态码不合意就提前返回而不读 body,HTTP/1.1 下连接无法交回连接池,只能被关闭
  • 📊 Node 基准:16KB 以下响应已被完全缓冲,读不读都一样;16KB 以上每忽略一次就多耗一条连接(10 个请求用 10 条而非 2 条)
  • 🌐 运行时差异:Chrome 可容忍 512KB 乃至更大的未读 body,比 Node 的边界高两个数量级,两边都依赖底层缓冲区大小,不能作为可靠保证
  • 🚫 cancel() 换不回连接:显式取消流与直接无视 body 的计数完全相同,只有读到末尾才能把连接还给连接池
  • 📈 失败是渐进的:不会卡死或报错,只是连接数增长、文件描述符堆积,客户端池随压力涨落,因此可在生产环境潜伏很久
  • 💧 根因是背压:网络层填满内部缓冲后停止从 socket 拉取,等待一个永远不会到来的读者
  • 👥 clone() 什么都不复制:规范定义就是"tee body's stream",相当于在仍在运行的管道上分叉,两个分支被绑在一起
  • 💾 代价是内存:60MB 响应下,放弃一个分支约多占 55MB(116.1MB → 169.3MB),与响应体大小相当
  • 📦 数据被保留而非重取:读完第一分支、等 500ms 后再读被弃分支,仍在 32.5ms 内拿到完整 60MB
  • ⏱️ 顺序规则:必须在任何 pull 之前调用 clone(),读取后再 clone 会抛 "Body has already been consumed";bodyUsed 按对象区分
  • ⚠️ 取消克隆的陷阱:await copy.body.cancel() 会死锁,它要等另一分支读完;不 await 也要等原分支结束才 settle
  • 🛑 Abort 比 promise 活得久:resolve 后 abort 仍会在下一次 body 读取时抛 AbortError,已到手的 chunk 依然有效;写入端会收到 aborted,是真正的断连
  • 🧩 便捷方法无法给出部分结果:arrayBuffer/json/text 被 abort 就直接 reject,想保留已收数据必须用 getReader()
  • ✅ 读取完成后 abort() 是空操作:不抛错,可安全放在清理路径,但调用成功并不代表真的取消了什么
  • ⏰ Promise.race 超时只覆盖头部阶段:慢 body 场景下 race 在 59.7ms 结束,请求却在 1251ms 才真正完成,200ms 预算在关键部分开始前就用尽了
  • ⏳ AbortSignal.timeout 覆盖全程:作为请求信号传入并附着到 body,在哪个阶段超时就在哪个阶段生效,慢 body 与慢 headers 都在约 200ms 被切断
  • 🖥️ Chrome 后台标签页陷阱:后台标签的 setTimeout 被钳制到约 1 秒,200ms 预算要 800ms 后才触发,正确行为看起来像坏了
  • 🔗 组合信号:用 AbortSignal.any 合并调用方信号、转换器信号、超时信号与内部控制器信号,任一方触发都会中止请求
  • 🔄 读取 body 才是真正的工作:也是这一切的正面价值——仍是活跃流才能被 pipeThrough 观察进度,示例中 promise 在 52ms settle,25%/50%/75%/100% 分布在随后的 1257ms 里
  • 📐 包装很重要:pipeThrough 返回的是流,需用原 status 与 headers 重建 Response,下游调用方对此毫无感知(ffetch 的 download-progress 插件即此思路)
  • 🧭 结论:await fetch(url) 应读作"头部已完成、body 即将开始"的那一刻;连接、内存、取消与超时都属于它之后的部分,之后写的代码才决定这一部分如何收场

在 Next.js 应用中根据自定义指标构建仪表盘和告警

本文介绍如何在 Next.js 应用中通过 Sentry 从自定义指标出发,快速搭建仪表盘与告警。只需从 Server Action 中发出一个自定义指标,就能把它变成可视化仪表盘组件,并配置阈值或异常检测告警;所有数据都与追踪关联,指标异常时一键跳转到对应的请求。教程约需 20-25 分钟,共 6 个步骤,适合具备 Next.js Route Handlers 或 Server Actions 基础的开发者。

  • 🚀 前提条件:需要一个 Next.js 应用(推荐 v13+ 的 App Router)、@sentry/nextjs 10.25.0 或更高版本(该版本起指标默认开启),以及 Sentry 账号和项目;使用异常检测告警需 Trial、Business 或 Enterprise 套餐
  • 🛠️ 第一步:安装 SDK:在项目根目录运行 npx @sentry/wizard@latest -i nextjs,向导会自动配置 instrumentation 文件,指标默认开启,如需关闭可设置 enableMetrics: false
  • 📊 第二步:发出自定义指标:用 Sentry.metrics.count() 统计计数类事件,用 Sentry.metrics.distribution() 记录需要百分位的数值;示例中每次用户增删演讲都会统计 schedule.event,并附带 action 和 result 属性
  • 🔍 第三步:在指标浏览器中查看:触发几次代码路径后打开 Metrics explorer,选择 schedule.event,按 result 属性分组查看成功、重复和失败情况;Samples 标签页可查看单个指标事件并直接跳转到对应追踪
  • 📈 第四步:搭建仪表盘:从浏览器中点击 Save As 将查询固定为仪表盘组件,再加入第二个相关指标(如 ai.tokens.total 的 p95 或 page.view 吞吐量)并排展示
  • 🚨 第五步:添加阈值告警:通过 Save As 菜单将其转为 Monitor,选择 Threshold 检测方式并设置明确的数值(如每小时 token 费用上限),通知可发送至 Slack、PagerDuty 或邮件
  • 🤖 第六步:改用异常检测:当尚不清楚"正常"基线时,创建第二个告警并选择 Anomaly 检测方式,Sentry 会学习指标的季节性和日/周规律,只需调整灵敏度滑块,适合流量自然起伏的指标
  • 💡 命名建议:使用一致的点分命名约定(如 schedule.event、ai.tokens.total),便于团队查找和分组
  • 💡 善用属性:把业务上下文(action、result、套餐等级)放入 attributes 而非指标名称中,高基数事件的属性不额外收费,且能提供更多切分和告警维度
  • 💡 百分位用分布:需要 p95 或 p99 时应使用分布而非 gauge,因为 gauge 只保留最新快照
  • ⚠️ 常见误区:不要把维度写进指标名(如 schedule.event.success),否则会爆炸基数导致无法分组;不要指望从 counter 或 gauge 中得到百分位;不要在强季节性指标上使用静态阈值;SDK 版本低于 10.25.0 会静默忽略指标调用
  • ❓ 常见问题:指标无需额外配置;count 用于计数、gauge 用于即时值、distribution 用于百分位;静态阈值适合已知危险数值,异常检测适合无基线或强季节性场景;指标按量计费,免费套餐足够入门;阈值和异常告警均可通知 Slack、PagerDuty 或邮件
  • 🔗 后续方向:可进一步探索 Tracing(分布式追踪)、Logs(结构化日志),并查看本教程基于的 Sentry 工作坊示例应用源码

面向比人类审查速度更快的 frontend 项目的 10 个反 AI 垃圾内容举措

本文面向 TypeScript/React 与 AI agent 开发,提出 10 项对抗 AI 生成“看似可用实则难查”代码的工程措施。作者把检查分成三类:避免多余代码被写出、找出已被写出的问题、通过 CI 强制落地,并讨论了成本、渐进式推行顺序,以及自动化无法取代的人类判断。

  • 🧾 OpenAPI 契约先行:前后端共用权威 API 规范,自动生成类型、客户端与 Zod schema;AI 猜错字段(如 user.fullName)会直接编译失败,审查重点回到小得多的契约本身。
  • 🔒 开启严格 TypeScript:strict: true 并加上 noUncheckedIndexedAccess、exactOptionalPropertyTypes,强制处理“值可能不存在”的情况,减少 AI 代码里常见的乐观假设。
  • 🧼 把 linter 当作行为过滤器:sonarjs 查复杂度/重复逻辑;react-you-might-not-need-an-effect 拦多余 effect;Vitest/Playwright 插件抓无断言测试和 .only;jsx-a11y 查无障碍;再禁用 any、非空断言等捷径。
  • 🧱 用依赖边界保护架构:通过 eslint-plugin-boundaries 或 dependency-cruiser 声明组件/服务/工具/设计系统间的依赖方向,阻止 AI 每轮会话都“向 GitHub 平均架构漂移”。
  • 🛠️ 把踩过的坑写成自定义 lint 规则:AI 让自定义规则十分钟内可完成,规则应源于真实事故,例如“service 返回前禁止 Math.round/toFixed”,避免二次舍入 bug。
  • 📏 把规则前置到模型生成阶段:把规则写进项目规则文件或 skill,让 agent 边写边看到指引;但这类预防只是概率性降低风险,不能替代事后检测。
  • 🧬 用变异测试检验测试是否真的有效:Stryker 故意改动代码,找出没有任何测试会发现的变化;存活变异往往指向只执行不验证、快照未核实、断言 mock 而非代码的测试。
  • 🪤 用 Knip 与自定义脚本清除死代码:Knip 找无人引用的文件/导出/依赖;自定义脚本可遍历入口图找不可达代码,或检查过期的 vi.mock(),把“不知道有多少问题”变成可度量的数量。
  • 📑 用 jscpd 抓重复代码:AI 倾向新写一个实现而不是搜索已有实现;jscpd 按不同阈值检测文本重复,JSX 可宽松、业务逻辑应严格。
  • 🚥 让每项检查都在 CI 中成为门禁:format/lint/tsc/unit/mutation/build 各自独立运行,分支不过就不能部署;唯一不可跳过的检查才真正有效。
  • 💸 注意成本并渐进式落地:误报、过期规则、运行时间和新人理解成本都真实存在;应先以 warning 建立基线并只扫改动文件,再逐条提升为 error,最后才上 CI 门禁。
  • 🧠 自动化不能替代人的理解:十项全绿只代表没有触发已知问题;功能对错、抽象时机、架构品味和产品判断仍需人工负责,这套机制是用来减少人工审查范围,而不是取消审查。

工具

serve-sim

  • 🚀 serve-sim 被称为 Apple Simulator 的 npx serve,可把已启动的模拟器托管给 Codex、Cursor、Claude Desktop 等 Agent 工具,支持本地、局域网、远程 Mac 与隧道访问。
  • 🎥 它通过 Swift 助手用 simctl io 捕获模拟器帧缓冲,提供 MJPEG 流、WebSocket 控制通道和 React 预览 UI,无需 Xcode 插件或 App 埋点。
  • 🕹️ 支持浏览器中 60 FPS 视频流、底部上滑回主屏、Option 键捏合缩放、键盘与热键转发、拖放图片/视频、日志转发和事件面板,并支持 iOS、iPad、Apple Watch。
  • 🧪 主要用于先本地测试托管基础设施,远程托管时隧道化 URL;工具与 React Native 无关,作者来自 Expo。
  • ⚙️ 环境要求:macOS、Xcode 命令行工具(xcrun simctl)、Node 20+ LTS;运行 CLI 不需要 bun;相机注入助手需 macOS 14+;仅支持 Apple Silicon arm64,不支持 Intel。
  • ⌨️ Xcode 27 键盘输入需 Device Hub 运行且目标模拟器窗口可见并前台,可能还需辅助功能权限;可设 SERVE_SIM_DISABLE_DEVICE_HUB_KEYBOARD=1 禁用桥接。
  • 🖥️ 核心命令包括 serve-sim [device...] 启动默认 localhost:3200 预览,--no-preview 仅前台流,以及 gesture、button、type、rotate、ca-debug、memory-warning、event-log 等。
  • 🎛️ 常用选项有 -p/--port、-d/--detach、-q/--quiet、--panes、--fit、--theme、--codec、--list、--kill;可指定多个模拟器,或留空附加全部已启动模拟器。
  • 📷 相机注入:serve-sim camera <bundle-id> 可替换单个 App 的相机源,支持 placeholder、file、webcam,并可通过 camera switch、camera mirror、camera status 等热切换而无需重启 App。
  • 🧠 相机注入原理:主机侧助手把 BGRA 帧写入 POSIX 共享内存,注入 dylib 通过 DYLD_INSERT_LIBRARIES swizzle AVFoundation;每设备一个助手,可被同一模拟器多个 App 共享。
  • 🤖 Agent Skill 位于 skills/serve-sim,可教 Claude Code、Cursor、Codex CLI、Gemini CLI 等通过 CLI 驱动模拟器;可用 bunx add-skill EvanBacon/serve-sim 添加。
  • 🧩 集成方式:Claude Code Desktop 可通过 .claude/launch.json 配置;Expo 可在 metro.config.js 挂载 simMiddleware,访问 http://localhost:8081/.sim。
  • 🔌 可嵌入开发服务器:serve-sim/middleware 是 Connect 风格中间件,适用于 Metro、Vite、Next、Express;单端口/远程代理用 proxyHelpers: true,并需把 upgrade 事件交给 handleUpgrade,TLS 反代转发 X-Forwarded-Proto。
  • 🏗️ 工作原理:iOS Simulator → simctl io → serve-sim-bin(Swift)→ MJPEG/WS → 浏览器;状态文件在 $TMPDIR/serve-sim/;Swift 助手独立运行,CLI 通过 bun build --compile 嵌入,npm 安装即可。
  • 📦 开发与许可:使用 bun install、build、build:swift、dev;许可证为 Apache-2.0;仓库约 2.8k stars、148 forks、161 commits。

fast-check

fast-check 是由 dubzzz 创建并用 TypeScript 编写的 JavaScript/TypeScript 基于属性测试框架,类似 QuickCheck。它通过随机生成输入来验证代码属性,并在失败时自动收缩到最小反例,适合单元测试、模糊测试、模型测试与竞态检测。

  • 🧪 核心定位:用于 JavaScript/TypeScript 的属性测试框架,验证“对所有满足前置条件的输入,断言都应成立”。
  • 📦 安装方式:支持 pnpm、yarn、npm,例如 pnpm add -D fast-check。
  • 🔌 测试集成:可配合 AVA、Jasmine、Jest、Mocha、Tape 等测试框架使用,并附有 Mocha 示例。
  • 🐞 失败诊断:属性失败时会报告种子、路径、收缩次数等信息,帮助快速定位实现问题。
  • 🧠 核心优势:强类型、TypeScript 友好,提供 map、chain、fc.pre(...)、fc.gen() 等扩展能力。
  • 🧩 智能生成与收缩:默认偏向同时生成小值和大值,支持 fc.oneof 收缩,更易找到反例。
  • 🛠️ 调试能力:支持 verbose 模式、最小反例重放、自定义样例、fc.context 日志记录。
  • 🏗️ 高级测试:支持模型测试,可用于测试 UI、API、状态机,并检测异步代码中的竞态条件。
  • ✅ 广泛信任:被 Jest、Jasmine、fp-ts、io-ts、Ramda、js-yaml、query-string 等项目使用,并帮助发现过开源项目 bug。
  • 📊 仓库数据:约 5.1k stars、212 forks、58 issues、14 PR、6,814 commits,采用 MIT 许可证。
  • 🧾 兼容性:4.x 需要 Node ≥12.17.0、ES2020、TypeScript ≥5.0;旧版本支持更低环境。
  • 🤝 社区贡献:拥有大量贡献者,遵循 all-contributors 规范,欢迎各种形式的贡献。
  • 💸 赞助支持:可通过 GitHub Sponsors 或 OpenCollective 赞助项目。
  • 📚 文档资源:提供入门教程、内置/自定义 arbitraries、属性运行器、API 参考和示例。

react-devtools-cdt-mcp

这是 React 仓库中 react-devtools-cdt-mcp 包的说明,属于实验性浏览器库,用于把 React 检查与性能分析工具注册到 chrome-devtools-mcp,本身不是 MCP 服务器,需在页面中于 React 之前导入,并依赖实验性第三方工具支持。

  • 🧪 实验性库:基于 chrome-devtools-mcp 的实验性第三方开发者工具 API,用于向 MCP 客户端暴露 React 检查与性能分析能力。
  • 🌐 浏览器库定位:需在受测页面中导入,chrome-devtools-mcp 会从页面发现 React 工具;它不是 MCP 服务器,不应放入 MCP 客户端配置。
  • 📦 安装方式:npm install react-devtools-cdt-mcp。
  • ⚙️ 使用要求:在 React 之前导入 react-devtools-cdt-mcp/register,以确保 DevTools hook 先于 React 初始化安装;该入口在非浏览器环境会抛错。
  • 🔌 自定义目标:包根无副作用,并导出 register、buildToolGroup 等底层 API。
  • 🛠️ MCP 配置:需在 MCP 客户端配置中加入 --categoryExperimentalThirdParty=true,并要求 chrome-devtools-mcp 1.3.0+。
  • 🔍 工具调用:React 工具会出现在 list_3p_developer_tools 中,可通过 execute_3p_developer_tool({toolName, params}) 或 evaluate_script(window.__dtmcp.executeTool(toolName, params)) 调用。
  • 🔐 隐私提示:这些工具可能向 MCP 客户端暴露组件 props 和 hook 值,仅建议在本地或可信调试会话中使用。
  • 🆔 UID 约定:组件以稳定 uid(如 r5)标识,跨工具和重渲染保持一致,但页面重载后失效。
  • 📤 输出约定:工具返回普通 JavaScript 值;失败时返回 {error: string}。
  • ⏱️ 时长约定:性能分析时长单位为毫秒;若构建未收集性能计时则返回 null。
  • 🌳 react_get_component_tree:获取组件树快照,支持 depth 和 rootUid,返回节点数组。
  • 🔎 react_get_component_by_uid:获取单个组件详情,可选 includeHooks;检查 hooks 会重新渲染组件渲染函数,但不运行 effects。
  • 🧩 react_get_component_by_dom_element:根据页面 DOM 元素引用获取对应 React host 组件详情。
  • 🔤 react_find_components:按名称子串不区分大小写查找组件,支持分页和限定子树。
  • 📍 react_get_component_source:获取组件定义源码位置;host 组件或生产构建可能返回 null。
  • 🧵 react_get_owner_stack_trace:获取原始 owner 栈追踪,仅 DEV 环境有值。
  • 🪜 react_get_parent_stack:获取已渲染父级列表,从直接父级到根,包含 host DOM 组件。
  • 🧭 react_get_owner_stack:获取结构化 owner 列表,表示通过 JSX 创建该元素的组件链;仅 DEV 可用,owner 不等于结构父级。
  • 🟢 react_start_profiling:启动性能分析会话,记录每次提交的渲染耗时,可选 traceName。
  • 🔴 react_stop_profiling:停止当前性能分析会话,返回 trace 名称和提交次数。
  • 📊 react_get_trace_overview:获取某次 trace 的逐提交概览,包括渲染、布局、被动耗时和变更组件数。
  • 📈 react_get_commit_report:获取单次提交的详细报告,组件按 actualDuration 降序排列。

memlab

memlab 是一个用于检测和调试内存泄漏的工具,支持浏览器、Node.js 和 CI 环境,提供自动化泄漏检测、堆分析、可视化调试及 AI 集成等功能,工作原理是通过定义场景、运行测试并追踪引用链来定位泄漏。

  • 🔍 浏览器泄漏检测:通过 Puppeteer 驱动应用,自动对比堆快照并聚类真正的泄漏对象。
  • 🧩 堆遍历 API:提供面向对象的快照访问接口,支持 Chromium、Node.js 等,可构建自定义泄漏检测器。
  • 🛠️ 内存 CLI 工具箱:内置重复字符串、超大对象和无限增长等分析,帮助发现优化机会。
  • 🖥️ MemLens 调试:在浏览器页面内直接可视化并交互式调试泄漏,无需离开测试页面。
  • ✅ Node.js 断言:单元测试可捕获自身堆快照并断言对象已释放,在 CI 中提前发现回归。
  • 🤖 MCP 服务器集成:可将堆快照交给 Claude Code 或 Cursor 等 AI 代理,用自然语言调查内存问题。
  • 📝 三步工作流程:编写测试场景(如点击操作)、运行 memlab CLI、通过引用链报告泄漏位置。
  • 🔗 引用链追踪:从 GC 根节点到泄漏对象完整展示引用关系,聚合相似泄漏,减少修复数量。
  • ⚡ 快速上手:使用 npm install -g memlab 安装即可开始泄漏检测。

更新

发布 pnpm 12.4 · pnpm/pnpm · GitHub

pnpm v12.4.0 发布,重点支持在同一工作区管理 npm、Python 与 Cargo 依赖,新增 pnpm pipeline 任务编排,并带来大量锁文件、注册表、Windows 和跨平台修复。

  • 🐍 支持 Python 依赖:启用 python.enabled,用 pnpm add pypi:<package> 安装,支持 pyproject.toml、pylock.toml、托管 .venv 及冻结/离线安装。
  • 🦀 支持 Cargo 依赖:启用 cargo.enabled,用 pnpm add crate:<package> 安装,支持 crates.io、自定义 sparse registry 和凭据认证。
  • ⚙️ Python 与 Cargo 均可通过 pnprServer 加速解析,不支持时回退本地解析。
  • 🧩 新增 pnpm pipeline [name]:安装冻结依赖并运行 pipelines 中声明的任务图,任务失败后仍继续执行。
  • 👀 pnpm pipeline --dry-run 可预览任务图,不安装配置依赖或运行 workspace hooks。
  • 💻 新增平台支持:Android arm64/x64、FreeBSD x64、Linux ppc64le、s390x、RISC-V。
  • 🧹 新增 trustPolicyExcludePrune,可在 add/update/remove 时自动清理未使用的 trustPolicyExclude,默认关闭。
  • ✅ 新增 pnpm change check,用于 CI 校验包版本是否符合 versioning.epics 和 versioning.fixed 分组。
  • 🔐 注册表元数据按 URL 路径与协议隔离,避免混用不同 registry 的版本、tarball 或 HTTP/HTTPS 元数据。
  • 🧾 pnpm cache view 现在显示完整 registry URL,相关解析脚本需要更新。
  • 🛠️ 新增构建脚本或 binding.gyp 的补丁会触发构建审批;支持 --allow-build=!<pkg> 和 approve-builds 保存决定。
  • 🌐 .npmrc 中的 registry 优先于 pnpm login 保存的全局配置,修复登录后使用错误 registry。
  • ⏱️ fetch-timeout 改为限制无进展时间,慢速大文件下载不再超时。
  • 🚀 提升大型工作区安装、pnpm deploy、warm global virtual store 等场景性能,并修复相关锁文件错误。
  • 📦 修复 pnpm add --workspace <pkg>,支持 workspace: 协议;add/install 接受 jsr:、npm:、workspace: 等协议前缀。
  • 🔁 布尔标志支持显式值,如 --prod=false;pnpm install <pkg> 支持 --offline 与 --prefer-offline。
  • 🧷 修复 frozen-lockfile、符号链接锁文件、lockfileDir 版本固定、pnpm import、patch-commit、版本范围上界等问题。
  • 🪟 修复 Windows 下 pnpm setup、命令 shim 重试、shellEmulator 参数转发、store 路径反斜杠等兼容问题。
  • 🔍 其他改进:audit 汇总排除忽略项、pack --json 错误 JSON、outdated -r 表格换行、shell 补全支持 pn 别名、pnpm version -m 短别名。

React DevTools 8.0.0

React DevTools 8.0.0 于 2026 年 9 月 8 日发布,重点是将 Suspense 标签页默认开启、移除 Timeline Profiler 并转向浏览器 Performance 面板,同时让 Elements 面板能显示对应 React 组件。本次更新还包含大量功能改进与错误修复,涉及 Profiler、Suspense、搜索导航、连接、过滤器、CSP 兼容性等。

  • 🧩 Suspense 标签页默认开启,可直接检查组件挂起原因,无需额外开启。
  • ⏱️ 移除 Timeline Profiler 标签页及相关代码;React 性能分析改用浏览器 Performance 面板中的 React 轨道。
  • 🔍 在浏览器 Elements 面板检查 DOM 节点时,可看到匹配的 React 组件,并新增 React Element 窗格。
  • 📊 Profiler 空状态围绕主操作重新设计;新增提交视图组件搜索、开始/停止分析快捷键、提交导航快捷键、父级堆栈工具。
  • 🧵 Suspense 标签页支持列出命名 Activity、按结束时间排序时间线、与主文档同步滚动;Activity 子树在隐藏模式下显示并变暗。
  • 🛠️ 新增组件搜索结果直接导航、忽略列表堆栈帧披露、显示 React.optimisticKey、允许重命名 Host Component props。
  • 🌐 支持 React DevTools 客户端连接自定义 host/port/path;在 sandbox CSP 页面中提供最小支持;在 React 检测前创建扩展面板。
  • 🔄 初始加载即应用组件过滤器,并在过滤器变化时更新检查元素;改进 changed hooks 检测;禁用 Strict Mode 日志变暗设置。
  • 🐛 修复 fallback Fiber 内容协调、嵌套 HOC 名称提取、prerender 时连接/重连、WhatChanged 滚出视图、控制台占位符与转义、独立错误 HTML 注入等问题。
  • 🐛 修复 -Infinity 保留、鼠标离开 DevTools 清除高亮、空 Suspended By 区块、旧 React 页面忽略新生产 renderer、类组件错误模拟崩溃、ContextMenu 空列表崩溃、AMD 模块、hoistables 内存泄漏。
  • 🐛 修复性能分析相关问题:断开子树时长、过滤 Activity 子节点崩溃、过滤节点误报重渲染、初始操作提交树构建、切换 profiler 根时提交索引重置、console 链接、useSyncExternalStore hook 索引、Activity slice 解码等。

Vitest 5.0

Vitest 5 正式发布,这次大版本以性能为核心,带来了全链路的速度优化和多项新功能,如 Trace View、嵌套项目继承、vi.when 条件 Mocking、全新的 Benchmarking API 等,同时收紧了默认行为和断言检查,并引入了一些破坏性变更(要求 Vite ≥6.4 与 Node ≥22.12)。

  • 🚀 性能提升显著:基于多套基准应用测量,vm 池、Browser Mode 和大型隔离套件速度提升明显,如 deps-heavy 提升 53%,大型 monolith 提升 19%。
  • ⚡ 内联项目如果没有修改 Vite 配置,会复用声明它的 Vite server,共享文件只转换一次。
  • 💾 文件系统模块缓存(fsModuleCache)已稳定,可在多次运行和不同 Vitest 进程间复用转换后的模块。
  • 🔄 主进程与 worker 之间减少往返通信,预热模块一次即可送达 worker。
  • 🧪 vmThreads/vmForks 重写:跨上下文复用编译代码、预预热模块图,并支持 require(esm)。
  • 🌐 Browser Mode 更快:预打包自身运行时、启动 Vite 时预热浏览器、自适应打开浏览器会话,并减少每文件往返。
  • 📦 安装体积更小:Vitest 现在打包自己的依赖,减少了 node_modules 数量和解析时间。
  • 📊 覆盖率更快:v8 provider 有界内存合并与预编译 glob,istanbul 迁移到维护的 @vitest/istanbuljs 包。
  • 🕵️ 新增 Trace View(Browser Mode):记录每个交互、断言和 page.mark 的 DOM 快照,可在 UI/HTML 报告中逐步回放调试。
  • 📂 嵌套项目与继承增强:内联项目默认继承根配置,被引用的配置可作为容器定义自己的 projects,--project 支持 -p 简写。
  • 🎭 新增 vi.when API:按参数(深比较 + asymmetric matchers)为 spy 定义不同返回值或拒绝行为,并支持 thenReturnOnce/times 限制。
  • ⏱️ Benchmarking 重写:bench 作为 test-context 夹具,可在普通 test() 内使用并参与 fixture/断言;支持 writeResult、bench.from() 基线对比和自定义 provider。
  • 🌳 Locator 找不到元素时显示 ARIA 树,方便调试;locators 默认真实匹配(exact: true),避免 getByText('Item') 误匹配 "Item 1"。
  • 🕰️ Fake timers 现在会同时模拟 Temporal API(基于 @sinonjs/fake-timers v15.4),并可通过 toNotFake 关闭。
  • ⚠️ 异步断言(resolves、rejects、toMatchFileSnapshot)未 await 时直接失败;expect.poll 超时会 reject,并提供 AbortSignal。
  • 📝 断言与类型系统强化:Matchers 接口引入返回类型和接收类型参数,自定义 matcher 可精确匹配期望参数类型。
  • 🧹 clearMocks 默认启用:每个测试前自动 vi.clearAllMocks(),避免调用历史跨测试污染;可用 clearMocks: false 恢复旧行为。
  • 📁 报告器与附件统一输出到根目录下的 .vitest 目录,HTML reporter 新增 singleFile 选项生成单文件报告,方便上传 CI 产物。
  • 🔁 CLI 新增 --repeats 循环运行所有测试,用于定位 flaky;还提供 vitest doctor 自动推荐加速配置。
  • 🔻 其他改进:新增 injectCjsGlobals 开关、coverage 支持子进程/worker 跟踪、junit 命名兼容选项、test.for 支持非 ASCII 等。
  • 🔨 破坏性变更:要求 Vite >=6.4.0、Node.js >=22.12.0;建议升级前阅读详细迁移指南。

Motion 是你本不会注意到的部分

动效是用户界面里“不应该被注意到”的关键细节——它让人明白刚刚发生了什么,让操作连贯流畅。作者将自己的博客比作一块好却煎坏的牛排,并围绕六个细节进行动效打磨,让网站从“能用”变成“真正有质感”。

  • 💡 动效的本质:它的存在是引导用户感知界面变化,而不是为了炫技;缺少动效时,每个操作都会变成生硬跳变,像一台“没有上油的生锈机器”。
  • 🧾 列表入场改为整体级联:文章列表不再由每一行各自淡入,而是作为单一整体交错进入,行间间隔约 50ms,约 0.5 秒内完成。父组件控制间隔,子组件只描述自己的动画,因此即使列表被过滤或重排也不会错位。
  • 🔍 搜索对话框加入过渡:打开时从 96% 缩放淡入,背景稍快变暗以将视线引向面板;关闭动画更短促。借助 AnimatePresence 让退出动画成为可能,同时修复了点击外部无法关闭、以及焦点未正确落入输入框的问题。
  • 📉 头部导航向下收缩让开:向下滚动时头部缩小约 6% 并略微上提,回滚时恢复。通过滚动位置驱动 MotionValue 而不是 React state,避免每次滚动都触发重渲染;并且用迟滞区间避免在临界点反复切换,且只是视觉缩放,不改变实际布局。
  • 📊 文章阅读进度条:页面顶部新增一条 2px 细线,表示阅读进度。使用 useScroll 指定文章容器为测量目标,而非整个页面,并附加弹簧平滑处理;不显示百分比数字,避免干扰阅读,同时保证文章结束时进度条准确停在满格。
  • 🖱️ 统一所有卡片的悬停反馈:之前每种卡片都有不同缩放和速度,作者将效果统一为“卡片上移 2px+ 图片放大到 1.03”,使用纯 CSS 实现。并通过 @media (hover:hover) 限制为真正的鼠标设备,避免触屏卡死在悬停态;同时让 :focus-visible 获得完全一致的视觉反馈,确保键盘导航也不被忽略。
  • 🖼️ 页面之间加入视图转场:点击文章卡片时,旧的索引页和新的文章页会交叉淡化,且被点击的卡片图片会通过 View Transitions API 中相同的 view-transition-name 被浏览器识别,从而平滑移动到文章顶部图位置。等待新页面渲染完成后再启动捕获,否则转场会失效;不支持该 API 或开启减少动效时会自动回退为瞬时导航。
  • ✨ 真正的价值并不在单个功能中:这些改动不会有人专门感谢,也看起来“像浪费一个晚上”,但配合在一起,就构成了普通网站与“有人认真思考过每一个细节”之间的差别。写作、布局、颜色都没变,仍是同一块牛排,只是现在被恰当地烹饪,能尝到鲜甜的肉汁。

其他

科学分成两种。"科学 1"是一种理想化的追求真理,"科学 2"是实际的科学实践,更多是一种社交活动,要考虑社会和政治现实。

如果你是一个博士生,你必须知道,你主要从事科学 2,而不是科学 1。

-- 《博士生不要试图改革科学》


关于向上管理以及如何做

向上管理不是讨好上级,而是从领导视角出发,主动沟通、协同和支持,确保双方共同成功。它是区分同水平人才成长速度的关键能力,核心在于弥合自己意图与他人感知之间的差距,并通过预期管理建立信任。

  • 🧭 先理解上级视角:思考“什么样的下属会让领导放心又欣赏”,以此指导自己的行为。
  • 🤝 管理上级的实质:不是控制或迎合,而是看到领导看不到的团队实情,同时理解领导的处境与目标。
  • 🎯 胜任本职是第一前提:明确领导的期望,确保自己的优先级与团队方向一致,并努力把该做的事做好。
  • 🛡️ 成为可靠的人:主动汇报进度,不等催促;让领导能把任务交给你后不用再担心。
  • 📢 宁可多沟通,也不要制造“惊喜”:无论事情好坏都提前同步,问题出现时带着缓解方案汇报,避免因隐瞒失去信任。
  • 💡 带着方案说问题:说出风险的同时也要给出解决思路、所需支持,而不是只抛困难。
  • 🔁 用“复盘”替自己加分:下次见面先回顾上次达成的共识、各自承诺和进展,既体现负责,也促使对方履行承诺。
  • 🧾 把“自我品牌”当动作来经营:别人如何感知你的“做了什么、怎么做、为何做”比你自己怎么看更重要。
  • 🚫 别怕被当成“拍马屁”:管理上级是正当的专业技能;你的经理无论如何都会影响你,主动经营这段关系才最有益。