CSS 下划线为什么会在字母下断开我在 Markdown 阅读器里审一篇 TypeScript 文章时,发现目录链接的下划线总在 y 和 p 下面断开。切到博客,悬停时出现的下划线却能从字尾下方连着画过去。我以为博客用的是底边框,打开开发者工具才发现,两处都用原生下划线。当时阅读器的偏移是 0.17em,博客引入的样式用了 0.2rem。 这三行用的是同一套环境。Windows Chrome 157、设备像素比(DPR)2、悠哉常2026-11-09
用 font-synthesis 补齐缺失的粗体博客换成悠哉以后,深色正文里的加粗词很容易扫过去。我用字体扩展给 GitHub 也换了悠哉,那里的粗体却很显眼。两边都请求较高的字重,我就用一句话比较这五个加粗的字。 雨停以后,窗边的书页被风轻轻翻过。 浅色和深色都用 16px,每种主题的正文和粗体用同一个颜色。Light 搭配 Medium 或 Regular 时,差别偏轻。用 Regular 合成粗体后,「窗」和「页」的笔画间隙更紧,但这五个2026-11-09
系统字体的 font-weight 500 兼容性处理简体中文用系统字体时,font-weight: 500 在苹方 SC 上可以用到 Medium。在 Windows 上,如果命中的是微软雅黑或微软雅黑 UI,500 会落到 Regular,强调文字就和 400 的正文一样粗。Android 则要看版本、机型和当前的系统字体。 简体中文实际用到的字体 请求 500 时 iOS、macOS 上的苹方 SC 有 Medium。Apple 列出 Ultr2026-11-09
从一对 TypeScript 括号看 Prettier、Biome、dprint 和 Oxfmt在写 Nuxt 组件的根 Block 命名规则时,我从 .nuxt/types/components.d.ts 摘了一条声明。文章里的代码围栏会被单独交给 Prettier,检查器认为这里少了一对括号。下面保留原始写法和格式化后的写法,方便对照。Nuxt 组件名的例子 - CommonEmptyState: typeof import('../../app/components/common/em2026-11-09
我所理解的 Git 最佳实践从个人提交写到团队协作,说明怎样局部暂存、何时用 rebase 或 merge、怎样设置 PR 合入方式,以及需求如何提测和独立上线。后五篇讨论仓库归属、备份恢复、根提交重建、大仓库迁移和跨平台路径检查。最后用 Windows 检出失败与大小写冲突的例子说明怎样在提交前发现不兼容的路径。2026-11-06
我所理解的 Hexo 最佳实践文章多起来以后,我在 Hexo 里遇到的麻烦逐渐从「怎么生成一个页面」变成了「怎么继续整理已经发出去的内容」。中文文件名适合找稿,公开地址要照顾读者收藏的链接。图片在编辑器里应该能直接打开,连载也应该能从首页总览、归档和搜索分别进入。 由于篇幅问题,全文分成八部分来讲。 第一篇《用文件名管理文章,用稳定字段生成网址》从编辑器里难找旧稿说起,把中文文件名、发布日期和公开网址交给各自的字段,再用一篇旧2026-11-06
Windows 上把桌面的 code 目录迁到 F 盘,用目录联接保留原路径新硬盘装上后,我想把桌面上的 code 目录移到 F:\code。这里有两百多万个文件,除了 Git 仓库,还有 node_modules、构建产物,以及保存 Codex 规则和 Skill 的 codex-home。IDE 的最近项目、Codex 的工作区和一些本地脚本都记着 Desktop\code。如果只把文件搬走,这些入口就得挨个改。 Windows 的目录联接(junction)可以把 2026-11-06
我所理解的 UI 设计最佳实践我做前端时,经常遇到一种说起来简单、改起来很慢的问题。页面已经能用了,我却觉得哪里有点怪。让 Agent 调一版,它可能换了颜色、减了间距,真正妨碍使用的地方却还在。等我能指出自己刚才在做什么、在哪里被打断,修改通常就容易多了。 这个系列记录我在做博客、内部页面和桌面工具时,逐渐形成的设计判断。产品里的真实操作、公开设计稿里的局部做法,以及改完之后的实际表现,都可以拿来对照。 第一篇《把界面反馈说2026-11-05
我所理解的前端框架(React 和 Vue)最佳实践我用 React 和 Vue 写页面时,经常会遇到相似的问题。组件怎么组织、状态放在哪里、交互怎么验证,很多判断可以放在一起谈。我把两个框架的实践放在同一个系列里,遇到共通的问题就先讲做决定的依据,再看各自的 API、组件库和工具怎样影响具体做法。 目前有两篇,可以按手头的问题选读。 第一篇《用表格组件减少通用渲染和交互的维护成本》先区分通用表格能力与项目的业务规则,再分别给出 Vue 和 Rea2026-11-04
我所理解的代码风格最佳实践改一段陌生代码时,我通常先找到触发它的按钮、调用点或报错,再沿着名字和引用往里走。这个过程有时很顺,有时得先猜第三个参数的意思,再经过一层转发,最后还要弄清两个叫 profile 的值分别是谁的。写代码时省下的几行,维护时可能要花好几次跳转才能补回来。 我理解的代码风格,包含命名、调用方式、模块的公开入口,也包含状态放在哪里、目录怎样表达归属。格式可以统一交给工具。剩下的选择,我通常用一个具体问题2026-11-03
ESLint 怎样判断 Vue 组件的根 Block在浏览器里看到 common-empty-state,可以顺着这个名字找到 components/common/empty-state.vue。但把这个文件移到普通 Vue 组件库后,根 Block 往往写成 empty-state。两个项目的文件路径可以一样,命名却取决于组件所在的工程。 给 ESLint 加一个 mode 就能区分两套写法。遇到 monorepo 时,麻烦在于同一个 ESLin2026-11-03
从 Node.js 到 Go Web在 Express 和 Koa 中,路由回调接收 req、res 或 ctx,再自行读取参数。Memos 的 Go 服务方法却直接接收 Context 和生成的请求消息。要把熟悉的经验迁移过来,需要先看清这两层之间的 HTTP 路由、消息解码和协议适配。 这里选择开源笔记项目 Memos,源码固定在 b40d8a7b573774c52b022c8d6537d6baf2129dc0。这份提交的 or2026-11-03
我所理解的 JavaScript 最佳实践代码写下来之后,大部分时间是用来读的。排查一条记录为什么排错了位置,要先看列表沿着哪一份输入走。审查一次批量提交的改动,要找出刷新到底等了哪些请求。能在调用处找到答案,就少一次跳转,也少一个靠猜测补上的前提。 Agent 读代码很快,依然需要判断哪些顺序属于业务约定、哪些值必须共享、哪些分支会写入外部系统。这些关系能从代码里直接看出来,Agent 修改时就有更明确的边界,人工 Review 也更容2026-11-03
Math.sumPrecise 能精确到哪一步在浏览器控制台试 Math.sumPrecise([0.1, 0.2]),结果还是 0.30000000000000004。看到函数名里的 Precise,很容易以为这次终于能得到 0.3。但把输入换成一个大数、一个小数,再加上那个大数的相反数,结果就变了。 const values = [1e20, 0.1, -1e20]; console.log(values.reduce((total, v2026-11-03
我所理解的 Markdown 最佳实践一篇技术文档通常要经过几次交接。作者在编辑器里写,读者在网页上看,有人把示例复制到项目里,审阅者再把意见交回给作者。Markdown 让这些人可以围绕同一份文本工作,也让解析、检查和批注各有了自己的边界。 由于篇幅问题,全文分成六部分来讲。 第一篇《中文加粗为什么会露出星号》从露在页面上的双星号说起,看看中文标点怎样影响 CommonMark 的强调边界。 第二篇《给 Markdown 文档接入 2026-11-03
大型后台管理系统如何把业务操作做可靠大型后台管理系统看起来很简单。查一笔订单,改几个字段,勾一批商品点下架。开发过一些后台,也用过一些后台之后,我们发现,许多需要反复推敲的地方,都藏在这些熟悉的操作后面。 客服点了退款,页面提示超时。他需要知道钱退了没有,才能回复客户。与此同时,这个入口还要被不同团队开发、联调和发布。组件需要让客服看懂结果,接口需要让页面查到结果,灰度发布则要让旧页面也能继续处理同一笔退款。 以多商户经营后台为例。2026-11-03
从 Node.js 到 Spring MVC熟悉 Express 或 Koa,再看 Spring 的路由注解并不难;真正陌生的是框架替代码完成了多少准备:Controller 谁来创建,Repository 为什么只有接口,方法参数怎么得到值,返回一个字符串为什么能生成整页 HTML。这组文章围绕这些问题阅读 Spring Petclinic。 源码选择官方 Spring Petclinic,固定在 500158f732419217507c2026-11-03
我所理解的 TypeScript 最佳实践写 TypeScript 时,我通常让编译器推断局部变量和简单函数的返回值。到了接口、组件 props、模块导出这些边界,我会把约定写得更清楚:数据从哪里来,哪些字段可能缺失,调用方可以传什么。类型本身也有边界,外部数据需要在运行时校验,工具脚本则需要一条确实会执行的检查命令。 由于篇幅问题,全文分成六部分来讲。 第一篇《为什么选择 TypeScript》从浏览器脚本、Ajax 和 Node.js2026-11-03
给荣耀手机自定义一套线条图标主题不知道有没有人像我一样,喜欢极简的风格,而手机上花花绿绿的图标让人看起来很是混乱。 好在 Android 往往提供图标自定义功能,我们可以选择第三方图标库来替换掉默认的。但这些图标库往往不能覆盖所有应用,这就导致有些应用图标还是原来的彩色样式。另外就是商店里的图标库不一定满足自己的想法,有的太小,有的太大。针对这个场景,我开始自己研究做一个图标库。 我想保留荣耀自带的桌面,就找了一套现成的线条图标2026-11-02
从 Node.js 到 FastAPI会用 Express 或 Koa,再读 FastAPI 的接口,路由方法并不陌生。真正需要重新建立认识的是处理函数的调用约定:没有固定的 req、res 参数,函数可以直接声明登录用户、数据库会话、查询参数和模型请求体。框架从哪里取得这些值,又在什么时候执行校验、依赖和响应处理,是这组文章围绕的问题。 这里选择官方 Full Stack FastAPI Template 作为具体项目。它有用户、登2026-11-02
滚动卡顿复盘:隐藏浮层为什么还在定位阅读掘金小册时遇到一个滚动明显卡顿的页面。页面上没有展开菜单,滚动期间却能看到浮层定位函数持续执行。当次检查中,7 个已经隐藏的浮层仍在监听滚动。 沿实例创建过程继续追踪,可以定位到页面所用组件封装的一条生命周期缺口:隐藏元素的尺寸通知进入更新方法,在未检查显示状态的情况下创建 Popper,并开启滚动监听。Tooltip 关闭、销毁后,同一条路径还可能把实例重新创建回来。 谁在滚动时更新当次检查2026-11-02
把缓存放在计算之前:从 Workers Cache 到 Node.js 实现给动态页面加 CDN 时,一个很自然的期待是:第一次访问生成页面,后续访问直接返回已经生成的 HTML。页面内容几分钟都没变,查数据库、拼数据、渲染模板就不必每次重做。 但应用部署成 Cloudflare Worker 后,过去常见的缓存方式仍然需要先运行 Worker,再由代码查缓存。缓存虽然命中了,函数入口却照常执行。 看到 Workers Cache 的介绍,吸引我的就是这个变化:缓存可以放2026-11-01
从活字到字体源码我喜欢 Inconsolata 的字母、大括号和整行代码的阅读节奏,也希望常见操作符能像 Fira Code 那样连起来。直接换字体,会连同这些习惯一起换掉,于是有了 Ligconsolata Next。 Inconsolata 源码已经带有 !==、===、->、=>、>=、<-、<= 这组编程连字,但它们放在 dlig 这个可选特性里。编辑器的普通连字开关未必启用它,所以「字体里有」和「打开连2026-11-01
用 Vue 3 封装原生 dialog 和 popover编辑资料时,需要一个阻止背景操作的弹窗;邮箱旁边的说明按钮,又只需要展开一小段提示。这两种界面都浮在页面上,但对焦点、关闭方式和背景交互的要求不同。以前写组件,要自己处理这些行为,再借助 Teleport、遮罩和 z-index 解决显示位置。 浏览器提供的 <dialog> 和 Popover API 已经接走了其中一部分工作。模态 dialog 可以让背景变为不可交互,popover 可以原生2026-11-01
Vue 3.6 Vapor 迁移实践:编译、测试与回归这次把现有 Vue 项目从 3.5.44 迁到 3.6.0-rc.14,并在独立界面启用 Vapor。迁移涉及组件编译、应用入口、测试运行时和原有交互的回归,原来的业务处理可以继续保留。 最初从一个小型 Popup 开始,先验证兼容性,再把同一流程用于其他界面。后续又迁移了使用原生滚动条库的字体 Popup,以及保留 Nuxt、Element Plus 和 Reka 组件的搜索站。前者能完整采用纯2026-10-31
ESA Pages 体验之旅从一个 Nuxt 搜索页出发,记录 ESA Pages 的选型、交付、存储、SDK 适配与请求预算,再补上最初忽略的境外接入性能。十三篇文章串起固定出口 HTTPS 对照、完整 API 清单、香港 FC 应用验证与认证迁移,区分功能可用、实际访问质量和生产迁移条件,为免备案部署与后续内地方案提供可复核的依据。2026-10-29
ESM 产物为什么还有 require:CommonJS、external 与宿主加载器把 format 设为 esm 后,程序仍可能因为 Dynamic require 或找不到模块而失败。需要先看那一段依赖已被内联,还是只保留了留给宿主的引用。下面用固定的 esbuild/Node 实验区分模块格式、依赖实现与加载器;受限边缘运行时另按它实际提供的能力判断。 普通 CommonJS 可以内联,原生模块通常外置,动态路径还需要运行时资源与加载器。ESA 作为受限宿主的例子,单独核对2026-10-29
Node 程序怎样构建与交付:从本地链接到独立运行在开发机上能运行一个 Node 程序,并不代表把入口文件发给别人也能运行。本地链接、生产依赖、模板文件与运行时都可能留在开发仓库里。这里从交付要求区分语法转换、依赖打包和发布包装,再用隔离目录中的固定实验检查产物究竟带齐了什么。 Node 能直接执行代码,为什么还要构建平时说「构建」,很容易把几种事情混在一起。 操作 解决什么问题 是否把依赖合进一个文件 语法转换 把 TypeScript、JSX2026-10-29
Happy DOM 为什么会误判复杂的 not 选择器我给一个知乎增强 Chrome 扩展写单元测试时,要验证一条内容过滤规则。问答页里的课程推广卡应该隐藏,评论区里用同一个类名的卡片应该保留。测试把扩展编译后的 CSS 加载到 Happy DOM,再按 ID 取出两个节点,检查各自的 display。 扩展的 Vitest 配置用了 test.environment: 'happy-dom',所以测试文件可以直接用环境提供的 document 和 2026-10-28
Stylus 嵌套选择器的逗号为什么会编译错在 Stylus 0.64.0 里,把 .item 缩进写在 :is(.a, .b) 下面,生成的 CSS 会把一次 .item 拼进 :is() 的参数里。要找出这次多余的拼接从哪里来,需要看 Stylus 在展开嵌套之前怎样划分选择器组。 背景项目里的样式有时需要让几个容器共用一条子元素规则。例如,在 .a 或 .b 里隐藏 .item。用 Stylus 的嵌套语法写这条规则时,.a 里的元素2026-10-28
浏览器如何自定义默认字体在浏览器里改了默认字体,网页却仍显示微软雅黑;全局 CSS 覆盖又可能改掉艺术字和图标。「网页字体偏好」扩展可以为正文和代码分别选择本机字体,并按网站开关。换字体涉及浏览器的候选匹配,也涉及网页后来加入的内容和主题变化,安装方法与这些实现细节分成四篇记录。2026-10-26
我所理解的 UnoCSS 最佳实践从 Bootstrap 的组件样式、Yahoo 的 Atomic CSS,到 Tailwind CSS 和 UnoCSS 的按需生成,先看 CSS 复用的粒度怎样变化,再讨论为什么优先选择 UnoCSS,以及如何分别减少源码重复、共享 CSS 规则和控制产物大小。工程实践、工具兼容与 Wind3 到 Wind4 的迁移各有实例和核验方法。2026-10-25
技术资料里的图片怎么压缩:体积、文字和动画的取舍离线技术资料里的 3.8 万张图片占了 11.82 GB。先用 145 个样本比较 WebP、AVIF 和缩放,再处理整库,最终在保留原尺寸和动画时间轴的前提下降至 6.95 GB。记录小字可读性、色彩与透明度的检查,解释为什么长截图不能按最长边缩小,以及替换图片时怎样同步维护正文引用、来源指纹和原件备份。2026-10-25
Vite 插件顺序:从 CSS v-bind() 失效说起在 Vue 字体预览组件里,我想把固定布局和动态字体、颜色一起写进 @apply。项目已经接入 UnoCSS 的 Wind3 预设和 directives transformer,普通工具类能工作,构建也成功,但 apply 中的 CSS v-bind() 没有随组件状态更新。 后来发现,只交换 Vite 配置里 UnoCSS() 和 vue() 的位置,结果就变了。问题发生在两个转换步骤之间:U2026-10-25
第三方页面适配:CSS 先处理外观,JS 再修正行为打开一段异步加载的评论,某个词先以蓝色链接出现,随后又变成普通文字。最终结果没有错,但这一下颜色变化很显眼。深色弹窗先露出白底、搜索框先出现推荐词、内部滚动条先显示原生样式,也可能属于同一类问题:目标样式要等脚本发现节点、判断类型、写入标记之后才能生效。 做浏览器扩展和用户脚本时,我们通常不能修改原站组件,却可以提前声明样式。只要节点原本就带着足够的识别信息,外观便可以直接交给 CSS;需要解析地2026-10-24
浏览器能打开,Python 却报证书错误:一次 HTTPS 证书链排障整理博客资料时,Matrix67 在浏览器里能打开,Python 却报证书校验失败。排查发现服务器只发送了站点证书,客户端需要另找中间证书才能构建可信路径。记录 OpenSSL 取证、系统证书库与原生链构建的区别,以及使用 truststore 后默认 Python 也恢复成功的复测变化,说明服务端补链、客户端兼容和关闭验证之间的边界。2026-10-23
Chrome executeScript 为什么拿不到外层变量:注入函数的序列化边界扩展的 Popup 或 service worker 里定义了一个常量,再把用到它的函数交给 chrome.scripting.executeScript(),编辑器没有报错,普通函数调用也正常。真正注入页面时,却出现 ReferenceError。 问题出在执行边界:Chrome 传过去的是函数的序列化结果,原来模块的词法环境不会一起进入目标页面。 看起来是回调,实际已经离开模块const st2026-10-22
模块入口与 import 后缀:公开 API 和运行环境要分别判断import 路径同时表达两件事:调用方从哪个公开入口拿能力,以及运行环境怎样找到文件。把这两件事混在一起,容易把「所有地方都不写后缀」或「每个目录都建 barrel」当成统一规范。 我主导的项目里,会用目录入口管理稳定模块公开面;前端 bundler 源码通常省略本地源码后缀。维护其他项目时,先沿用它已有的明确规范和稳定写法。Node ESM 的解析要求则需要单独满足。 用 index.ts 声2026-10-22
单行省略为什么会裁掉字:line-height、字形和 overflow 的边界昵称和表格单元格常用单行省略。有时省略号正常出现,字母底部或重音却少了一截。开发者容易先改宽度,甚至换成 JavaScript 截字符串;真正需要看的往往是行高和裁切区域。 省略号只处理行内溢出常见的 truncate 对应 overflow: hidden、text-overflow: ellipsis 和 white-space: nowrap。它解决的是横向空间不足,没有承诺字形在垂直方向一2026-10-22
defineModel 已经声明了什么:避免重复 v-model 契约和默认值分叉在 Vue 3.4+ 中,defineModel() 不只是把一段读写代码写短。它已经声明了 model 对应的 prop 和更新事件。迁移旧组件时,如果只加这一个宏,却保留原来的同名 defineProps() 和 defineEmits(),同一个公开契约就有了两份声明。 先明确组件暴露的 model<script setup lang="ts"> const value = defineMo2026-10-22
环境变量怎么管理:共用配置、个人覆盖与框架差异一个项目里既有团队共用的 API 地址,也有个人调试覆盖和部署密钥。把它们都交给一份 .env,会混淆归属;照搬另一套框架的文件名,又可能遇到不同加载顺序和公开规则。 由于篇幅问题,全文分成 2 部分来讲。 第一篇《共用默认值、个人覆盖与部署配置》介绍了配置归属、共享默认值、本机覆盖、启动加载、Git 保护和构建/运行时差异,并用 Vite 与 Nuxt 的相同输入检查实际结果。 第二篇《前后端框2026-10-21
前端请求方案选型:fetch、Axios 与服务端状态管理fetch、Axios 与 Query 库都与请求有关,却承担不同职责。一次 HTTP 交互需要处理响应、错误、认证、超时和取消;远程数据在多个组件中的副本,还需要缓存身份、新鲜度与写后同步。两篇分别讨论请求客户端和服务端状态管理,再看它们怎样组合,避免重试相乘、重复错误提示与多份数据所有权。2026-10-21
从搜索动效原型到 Nuxt 应用:滚动、正文渲染和工程边界做这个搜索工具时,我先找了一份喜欢的动效参考:The Collaboratory — Refine Search。输入框从首页移到结果页,资料旁边有相关、不相关和收藏操作,选中的资料再进入细化面板。这套交互比单纯堆一个搜索框和列表更有意思,适合做一个可以逐步整理线索的小工具。 演示数据很容易把效果撑起来。真正接上搜索接口,问题才陆续出现:一篇长摘要让整个弹窗开始滚动,标题和关闭按钮一起消失;上游文2026-10-21
固定输入框里一打字,页面就往上跳:scroll-padding 与焦点滚动搜索结果页往下滚了一段,顶部搜索框仍然可见。点进去补几个字,下面的结果却跟着往上跳;继续输入,页面会一步步退回顶部。 这个问题出现在一个 Nuxt 搜索页面里。起初值得怀疑的地方不少:输入会更新字符计数,筛选条件显示成标签,Header 使用 sticky,页面还接了 OverlayScrollbars。测量以后发现,搜索框的位置和尺寸一直没变,变的是文档的 scrollY。 最终修复只有一行:删2026-10-21
桌面快捷方式为什么找不到脚本:一次 MSIX 文件系统虚拟化排查给 Windows 上的 WSL 装好 KDE 桌面后,我希望像打开普通软件一样,双击桌面的「Ubuntu KDE Plasma」就能进入 Linux 桌面。启动器负责唤起后台服务,再用 Edge 的独立窗口打开 noVNC。 桌面本身已经能用了,快捷方式却反复出问题:最早提示「无法找到脚本文件」,换成 CMD 启动器后,又表现为双击没有反应,或者命令窗口一闪而过。奇怪的是,让 Codex 执行同2026-10-20
在 WSL 中用 noVNC 操作 KDE Wayland:一次嵌套桌面的实践在 Windows 上装好 WSL 和 KDE,再把桌面放进浏览器,看起来已经很接近一台普通的 Linux 电脑:有任务栏,可以打开应用,也可以最小化、最大化窗口。 但在 4K 屏幕上用起来,问题接连出现。画面铺满了,字却不一定清楚;分辨率提高了,按钮又变得很小;在 KDE 设置里修改缩放,关闭 noVNC 再打开,界面还是老样子。 我因此想把原来的 KDE X11 会话换成 Wayland,继续2026-10-20
Happy DOM 和 jsdom:差异、选型与测试迁移从 jsdom 切到 Happy DOM 后,样式字符串、HTML 文档结构、构造器判断和网络行为可能一起变化。这些失败需要分别判断,不能靠更新 expected 获得通过。能力篇比较两种实现与真实浏览器的行为和测试职责,迁移篇继续排查 Document、mock、fetch 与时钟,在统一环境时保留业务断言和外部副作用控制。2026-10-19
Regex Tuesday 题解Regex Tuesday 是 Callum Macrae 做的一组在线正则练习。每道题都有一组输入和预期结果,可以直接在页面里填写正则,看哪些用例通过、哪些没有通过。题型分为匹配和查找替换两类,内容从重复单词、日期、URL,到元素符号、音乐和国际象棋记号,适合用来练习把文字规则变成正则。 偶然发现这个网站以后,我把里面的 20 道题逐一做完,也记录了解题时用到的规则和踩过的坑。最新一题发布于 22026-10-19
浏览器如何识别网页、图片和下载:MIME、Content-Type 与内容嗅探浏览器收到响应后,怎样决定渲染网页、展示图片,还是下载文件?MIME 类型提供声明,内容嗅探处理部分缺失或错误信息,加载场景与安全规则继续限制使用方式。结合 Chrome 实验,可以看清扩展名、类型响应头、下载指令和文档属性各自的作用,解释脚本类型报错、图片解码失败与上传校验中的常见误区。2026-10-18
如何获取 URL 参数:原生 API、工具库与路由的选型读取 URL 参数时,真正的差别在于重复 key、空值、编码、数组和业务类型怎样约定。原生 API、qs、query-string、ufo 与路由并不共享同一套默认协议。先用相同输入比较解析和序列化,再把参数接入路由与响应式状态,处理刷新、前进后退和持续写回,让读取方式与页面的数据所有权保持一致。2026-10-18
DNS 之前,浏览器在忙什么:Chrome Omnibox 原理与自定义地址栏输入一段文字后,浏览器需要判断导航与搜索意图,组织历史、书签、搜索和扩展候选,再执行用户确认的动作。这个过程决定了 DNS 与页面加载之前的目标选择,也决定了自定义入口应该接在哪一层。机制篇保留输入分类、候选排序和协议分流,实践篇集中说明站点快捷字、OpenSearch、扩展建议与企业策略的适用边界。2026-10-17
从 Git 到 Scion:AI 研发、大仓库与有效产出Anthropic 用 Scion 替换内部大仓库的 Git,引出的争议涉及版本控制与研发价值。从 Git 的对象模型和分布式优势,到 Google、Meta、Microsoft 的实践,再到三万次合并、两千五百万次 CI checkout 的口径,分析基础设施压力与 AI 返工如何同时存在,以及哪些证据能判断新增活动是否带来了有效产出。2026-10-17
正则为什么会卡死:ReDoS、非回溯引擎与 ESLint 护栏正则没有源码层面的死循环,仍可能在失败之前重复分配字符或移动搜索起点。风险取决于模式、输入、调用方式和实际引擎。机制篇从十六进制与短名称校验解释回溯、语义和非回溯引擎;工程篇再核对 ESLint 的诊断与漏检,把静态反馈和长度限制、执行预算、独立线程及终止清理放在同一条验证链里。2026-10-17
pnpm 为什么装到了旧版本:npmmirror 时间字段与依赖解析排查给博客接入 textlint时,规则还没来得及检查正文,Markdown 解析器先报了错:ERR_PACKAGE_PATH_NOT_EXPORTED。错误指向 micromark 的一个内部文件。 从子路径错误追到版本选择这次运行环境是 Node.js 25.8.1、pnpm 11.17.0,registry 使用 npmmirror。textlint 及其 Markdown 解析包都是 15.82026-10-15
设计组件库时需要考虑什么:从通用 UI 到业务组件面试里问「如果让你设计一个组件库,需要考虑什么」,很容易从 TypeScript、打包工具、按需加载一路讲到单元测试。这些都需要考虑,但在选工具之前,还有一个决定后续设计的问题:准备把什么东西复用起来? 按钮、下拉框复用的是视觉和交互;查询表格复用的是查询、分页、排序等页面模式;店铺选择器复用的则可能是店铺身份、可选范围、历史值回显和业务限制。它们都叫组件,承担的责任却不一样。 知乎这道关于组件库2026-10-14
最长递增子序列:从长度到具体下标最长递增子序列需要保持原数组的先后顺序,但不要求位置连续。只知道最大长度,还不能告诉 DOM Diff 哪些项可以保留;这又引出具体下标的恢复问题。下面从容易验证的动态规划走到二分结尾表,再用前驱记录找回一条真实序列。 LeetCode 300:从求长度到还原具体序列LeetCode 300「最长递增子序列」给定一个整数数组,要求返回严格递增子序列的最大长度。Diff 还多了一项要求:知道究竟保留2026-10-14
React、Vue 2 与 Vue 3 的 DOM Diff:从节点身份到最长递增子序列最近一道前端面试题问到了 React、Vue 2 和 Vue 3 的 DOM Diff。这个问题既涉及列表如何重排,也涉及更基础的更新:同一个按钮改了 class 与文本,是否重建 DOM;把 div 换成 section,原来的子组件状态还在不在;同一种组件收到新 props,和换成另一种组件又有什么区别。 这些场景需要先回答「旧节点与新节点是否属于同一个身份」,再决定更新什么。列表还多了一层顺2026-10-14
Vue 的 VNode 生命周期事件:在模板里监听子组件挂载Vue 有一些不太常见的写法。比如,我们可以直接在组件标签上监听它的挂载: <Child @vue:mounted="onChildMounted" /> 日常写组件时,生命周期通常出现在 mounted()、onMounted() 这些位置。模板里的 @ 除了监听点击、输入或子组件发出的业务事件,也能让父组件在使用子组件的地方,监听这个子组件对应节点的生命周期。 这个能力由 Vue 自身提供,官2026-10-14
无法接入虚拟滚动时,怎样减少长列表的屏外渲染一个不断加载内容的列表,滚得越久,页面里的卡片就越多。理想的处理方式是让列表组件只挂载视口附近的节点,也就是虚拟滚动。但有时列表由第三方组件或现成页面管理,我们只能给已有 DOM 加样式和少量脚本,碰不到它的数据源、分页状态与节点复用逻辑。 这种约束下,仍然可以减少一部分浏览器工作:保留卡片及其占据的空间,让浏览器暂时跳过远离视口的内容布局和绘制。负责这件事的是 content-visibilit2026-10-13
让 Codex 调用普通 ChatGPT:一次本地代理链路验证看到网罗灯下黑的《不加钱,把 ChatGPT 几乎无限的网页额度当 Codex 用》后,我想验证另一种接法:继续在本地 Codex 里读文件、执行命令、改代码,把模型请求转给普通 ChatGPT。 这样既不用把工作搬到网页,也能保留 Codex 的本地执行环境。CC Switch 可以帮忙改配置,不过我更想知道,能不能直接启动一个本地服务完成转发。 2026 年 10 月 5 日,Codex 通过2026-10-05
复用成熟 App 的主题配色:从原生模块到可复用数据QQ 的「地海蔚蓝」在深色模式下是一片灰蓝。配置里的第一项背景色 #628D9B 比聊天区中央的实测值 #48575C 鲜亮许多。若想把这种灰蓝带到阅读应用里,先要找出两种颜色之间经过了哪些绘制步骤。 这组文章以 Windows QQ 9.9.36-53644 和 Windows Codex 26.1103.4239.0 为例。QQ 有静态配色、由基础色生成的模板、纹理和深色遮罩。Codex 桌面2026-10-03
刷新后为什么会自动滚到下面:浏览器滚动恢复和无限流知乎首页刷新后停在靠下的位置,又触发了无限流加载。浏览器原生滚动恢复是一种可能原因,站点自身逻辑仍待排查。文章结合 HTML 标准和 Router 文档,解释旧位置怎样触发底部哨兵,以及 scrollRestoration、异步渲染、bfcache 和插件注入时机的边界。2026-09-30
从原神开小火车到 PID:让系统自己稳下来的控制算法从《原神》7.0 活动「程准列车驭法」里的挡位、限速、坡度和停车标记讲 PID:玩家怎样用反馈修正速度,P/I/D 分别看当前误差、长期偏差和变化趋势,为什么调参、积分饱和、微分噪声和输出限制会影响真实控制。2026-09-30
让浏览器插件调用本地能力:Native Host 的边界和协议浏览器扩展调用系统剪贴板、本地文件或 CLI 时,可以通过 Native Messaging 与本机程序交换带长度前缀的 JSON。跑通通信还需要 manifest、浏览器注册、运行入口与真实 ping;多个扩展复用 Host 后,则要另外设计能力分发、参数校验、结构化错误和权限策略。安装通道与能力协议分别成篇,保留各自的调试与安全边界。2026-09-27
浏览器复制 GIF 为什么总会丢动画:剪贴板格式和插件方案新版 Edge 里右键复制 GIF 到聊天工具仍可能只剩第一帧。问题不在动图文件本身,而在浏览器写入系统剪贴板时选择了位图、HTML、文件列表等不同格式。文章梳理 Chromium 现状、Clipboard API 边界、现成项目缺口,以及用扩展加 Native Host 修这个老问题的方案。2026-09-27
从 Vercel 的 Agent 教程开始:普通开发者怎么做一个能落地的 AI AgentVercel 的 Agent 教程把模型、工具和应用服务串在了一起。要把它用进自己的项目,还需要弄清模型与程序各自负责什么、工具怎样执行,以及任务何时停止。围绕一个客服售后助手,从概念讲到本地 Node.js 与 AI SDK 工程,再看权限、失败恢复和长任务怎样处理。2026-09-23
从 MacBook 的 AV1 开始:一部视频编码编年史对照 MacBook 媒体引擎时,AV1 decode 这一行引出了两个问题:设备播放视频时能省多少通用计算,编码格式又怎样减少传输数据。第一篇围绕硬解、平台选流和购买用途作判断,第二篇从空间、时间与感知冗余进入预测、残差、变换和量化,再沿视频标准与软硬件生态的演进解释这些收益为什么不同。2026-09-21
纠错码入门:数据出错以后,电脑怎样把它改回来从二维码、SSD、内存和无线通信里常见的纠错现象出发,用重复码、汉明码、软判决、LDPC、里德-所罗门码和香农极限串起纠错码的基本直觉,说明额外冗余怎样帮助系统发现和恢复错误,以及这些能力为什么总有边界。2026-09-07
没有设计稿时,把 DESIGN.md 写成 Agent 能执行的视觉约束没有 Figma 设计稿时,AI 很容易把前端页面做成临场审美。DESIGN.md 可以把颜色、字体、间距、组件状态和禁忌项写成 Agent 能读取的视觉约束;真正落地时还要判断来源、场景适配、项目 token 映射和浏览器验证。2026-09-02
VitePress sidebar 文案里一对尖括号引发的页面崩溃VitePress sidebar 的 text 字段会按 HTML 渲染。一次文档站线上点击失效,根因是分组名里的 script setup 被当成 script 标签解析。文章顺着 Issue 1486、PR 1489、上游 PR 5390 和默认主题源码,整理短期 HTML entity、长期 inline Markdown、现成 Lint 边界和本地护栏。2026-08-18
搜索高亮别急着包 span:CSS Custom Highlight API 的边界和取舍搜索高亮不一定要反复往 DOM 里塞 span。从实时搜索、长文档和编辑器高亮的问题背景出发,拆开 CSS Custom Highlight API 适合解决的绘制层问题、语义和兼容边界,并对比 mark.js、react-highlight-words、CodeMirror、Tiptap 等开源方案。2026-08-14
Vue KeepAlive 内部滚动恢复怎么封:库、生命周期和跨版本适配把 KeepAlive 页面里的内部滚动恢复拆成可复用方案:先判断现成库能覆盖到哪里,再把 Vue2 directive、Vue3 composable 和滚动容器解析接到同一份核心控制器上,用测试守住数据刷新回顶、用户打断和多容器边界。2026-08-14
Codex 只认一个账号时,浏览器登录态怎么组织记录 2026 年 8 月桌面入口只有一个当前账号时,怎样用浏览器保存不同账号的登录态。文章整理 ChatGPT Web、Codex App 和浏览器 Profile 的边界,给出 Edge/Chromium 专用数据目录的低频切换方案,并说明第三方多会话扩展为什么不适合作为首选。2026-08-12
给静态博客音乐播放器补一个单一播放页所有权静态博客普通跳转会销毁音频实例,新标签页又可能重复发声。这次用 localStorage 短租约和 BroadcastChannel 协调播放权,点击播放时通知旧页暂停,首页文章则新标签打开。方案减少多页面重复播放,但不保证强互斥,也不解决同标签页导航不断歌。2026-08-08
Windows 上 Node spawn 命令为什么会 ENOENT:从 cmd shim 到 execa外层 pnpm.cmd 已启动,dev server 内部的 spawn pnpm 却报 ENOENT。文章从这次故障说明 Windows CLI 的 .cmd、PATH 和 shim 边界,并按本地 bin、短命令结果与长进程清理需求,比较 shell: true、cross-spawn 和 execa。2026-08-04
Windows 上别直接相信 python 命令:给本地校验补一个跨平台入口记录一次 Windows 上 `python` 命令踩坑:系统的 App Execution Alias 可能把 python.exe / python3.exe 指到 Microsoft Store 安装入口。文章先讲清重定向原因、机器级修法和局部包装器边界,再说明为什么给 Skill 校验补一个跨平台 Node 入口。2026-08-03
GitHub SSH 克隆在 22 端口断开时,改走 443 端口记录一次 GitHub SSH clone 在 22 端口卡住 banner 阶段的排查:先用 `ssh -G` 和 `ssh -T -vvv` 区分 key 问题和网络问题,再把 GitHub 的 SSH 入口改到 `ssh.github.com:443`,并保留其他 Git 平台的默认配置。2026-07-28
把 Codex Home 迁移做成跨平台 Node 脚本Codex Home 迁移一开始容易写成 macOS 一套 shell、Windows 一套 PowerShell。更稳的做法是用一个零依赖 Node 脚本作为入口,把 symlink、junction、hard link、dry-run、备份、WSL 和 Skill 自带脚本的跨平台边界都收进实现里,让读者只面对同一个迁移命令。2026-07-27
把旧工具库迁到 es-toolkit:一次 sheng-tool 现代化改造旧工具库的依赖、构建和文档链路逐渐比函数本身更难维护。迁移先明确 es-toolkit 的使用边界、公开 API、模块与类型入口及 npm 文件清单,再保留注释即测试的设计,用 TypeScript compiler API 提取 JSDoc 示例。两篇分别关注发布消费者和测试文档链路,让类型、运行时语义与公开说明有各自的验证依据。2026-07-25
Skill 管不住代码风格时:把项目约定写成 ESLint 护栏Skill 和 AGENTS.md 能告诉 Agent 项目约定,却不能阻止代码再次偏离约定。哪些偏差适合 ESLint,哪些需要专项脚本,哪些仍应交给 review?文章从可检测性、维护成本、项目归属和迁移期误报出发,说明如何选择检查方式、配置范围与严重级别。2026-07-24
Composable 的公开面别靠默契:用 ESLint 守住私有模块边界记录一次 composable 边界治理:公开面、owner 和 Vue/Nuxt 上下文调用时机很容易漂移。文章把经验拆成五条 ESLint 规则,约束全局 source/action、局部 composable、use*.ts 主入口、私有子模块归属和上下文 API 顶层调用。2026-07-24
把无限滚动触底触发器抽成 composable:虚拟列表、哨兵和滚动兜底无限滚动最容易被写成一个 useIntersectionObserver demo:底部 sentinel 进入视口就请求下一页。但虚拟列表、短列表和接口状态变化会让这个模型失效。更稳的做法是把触底触发抽成 composable:哨兵观察负责常规场景,滚动距离负责兜底,数据变化后再等 DOM patch 和下一帧布局完成重新检查。2026-07-24
构建过了不代表 Nitro 能启动:给 server bundle 补运行时 import 检查Nuxt / Nitro 项目里,build 和 typecheck 通过不代表 server bundle 能在 Node 里启动。这个检查脚本在构建后扫描 `.output/server` 的裸包 import,并用 Node resolver 提前暴露 package exports 子路径缺失等运行时问题。2026-07-24
Skill 约束不了响应式尺寸时:用构建插件给 UnoCSS 自动补 lg 副本Agent 可以记住样式约定,但不能保证每次都写对响应式尺寸。文章复盘一个 Astro / UnoCSS 项目如何在 mobile-forever、@unocss/postcss、UnoCSS transformer 和 PostCSS plugin 之间反复试错,最后用构建插件自动补 lg 副本,并用测试守住配置顺序。2026-07-24
无限滚动为什么会跳到新底部:一次滚动快照恢复的复盘复盘一次普通 feed 无限滚动在分页追加时跳到新底部的问题:先看错误 watcher 如何把当前会话快照误当历史快照,再拆出滚动恢复闸门、触底加载 composable、回归测试和可复制源码包。2026-07-22
别等 review 才想起 SSR:给 Nuxt 模块级 composable 补上静态护栏记录一次移动端个人页吸顶 Header 重构:跨组件 DOM 状态需要浏览器会话级 source,但模块级状态会碰到 Nuxt SSR 风险。文章解释为什么不能只靠 Pinia、useState 或 .client.ts,并用 client-only source facade、ESLint 规则和测试把边界落下来。2026-07-16
状态归属比组件封装更重要:一次 Profile 外层错误边界重构Profile 页的外层 blocker 同时判断请求进度、最终错误和业务内容限制,职责难以区分。文章记录如何按状态归属划分加载、空态、内容和错误处理,区分 EmptyState、RenderErrorBoundary、StateHost 与主资源 composable,并提供源码和回归测试。2026-07-15
用 const enum 和字符串值类型保留公开 API 的自然写法组件和工具 API 里经常同时需要两种语义:内部代码希望用有名字的 enum 分支,外部调用方又希望直接传普通字符串。文章用 const enum 和 `${Enum}` 派生字符串值类型,把命名常量、公开字符串 API、lint 护栏和适用边界拆清楚。2026-07-14
当组件只有一个调用方:可选 props 为什么应该被 lint 管住一次 Vue 组件重构里,单调用组件仍然保留可选 props,导致父子契约变松、子组件继续写防御判断。文章记录为什么 optional props 是 API 契约,不只是默认值问题,以及如何用可复制的全仓组件调用图、defineProps AST 和架构 lint 把这类退化提前挡住。2026-07-14
Composable API 为什么要默认用对象边界业务 composable 和页面 helper 的入参、返回值默认用对象,核心价值是降低 API 演进成本。文章从位置参数、单值返回、对象解构和无参 resolver 四种写法出发,整理什么时候该用对象边界,什么时候可以保留底层工具式 API。2026-07-13
Nuxt plugin 抢跑 Pinia 为什么会把应用启动打挂一次 Nuxt 客户端插件为了抢在 WebSocket 建连前注册监听使用 pre 时序,结果早于 @pinia/nuxt 执行,启动阶段调用 useAppStore 触发 getActivePinia。文章复盘 plugin 顺序、Pinia 激活点、dependsOn 修复和脚本化 lint 护栏。2026-07-13
Nuxt 自动导入不该靠自觉:一次组件和 Vue API import 的 ESLint 护栏Nuxt 会自动导入组件和常用 Vue API,但手写 import 很容易在重构后留下冗余、deep import 和路径耦合。文章记录一次只用 warn、不做 autofix 的 ESLint 护栏:怎么限定扫描范围、对齐 Nuxt 真实组件名生成机制、放过非 owner 模块,并用扫描统计和测试支撑渐进治理。2026-07-09
别让一个 composable 承担两种身份:一次 Profile 全局状态重构记录一次 Nuxt Profile 页状态重构:`useMeProfile` 同时表示当前账号和路由用户,导致刷新链路、SSR 首帧和账号切换变得含糊。文章把 source owner 拆成账号身份、账号资料、原始数据和路由 Profile source,并补上 stale response、写后刷新和单测护栏。2026-07-09
跑马灯文字为什么会重叠:一次 flex clone 布局问题的复盘长徽章文字盖住了跑马灯的 clone:flex 压缩了内容布局盒,文字却仍向外溢出,clone 因而提前出现。文章复盘这次重叠的原因、共享轨道组件应承担的 CSS 契约,以及 Lint、单测和真实浏览器几何验证各自的边界。2026-07-08
不要用屏幕宽度判断触摸交互:一次照片网格拖拽排序修复照片网格在桌面端需要 hover 菜单和短按拖拽,在手机和平板上却需要长按菜单和长按排序。文章把问题拆成布局断点和输入能力两层,解释为什么不能用屏幕宽度判断交互方式,以及如何用 hover/pointer media query、SortableJS 触摸延迟和测试断言守住这个边界。2026-07-08
浏览器为什么播不了 App 语音:一次 AMR 互通和策略模式改造App 语音介绍在移动端能互通,到了 Web 却只转圈不出声。排查后发现,历史语音文件是 AMR,测试的 Chrome 原生 Audio 无法解码,资源 404 和 CORS 都不是根因。文章拆解 AMR 文件、媒体栈、播放与录音策略,以及 benz-amr-recorder 和 audio-decode 这两条库路线的取舍。2026-07-08
Vue 的 script setup 泛型组件到底解决了什么Vue 3.3 给 script setup 加了 generic 属性,让 SFC 组件也能像 TypeScript 函数一样把 props、emits、slots 和子组件透传绑定到同一个类型参数。文章结合官方文档、Vue Language Tools 源码和完整 ActionMenu 示例,解释多泛型怎么写、怎么被解析、怎么在调用方保留精确类型。2026-07-07
Vue watch 的 cleanup 到底清理了什么用个人主页距离展示里的异步回写问题解释 Vue watcher cleanup:`onCleanup` 清理的是一轮 watcher 失效后的副作用,不是组件卸载钩子;再对比 Vue 3.5 的 `onWatcherCleanup()`,给出定位 API、fetch 和 watchEffect 场景下的使用边界。2026-07-07
一个昵称为什么这么难:emoji、PAG 特效和跑马灯彩色昵称看起来只是一个文本组件,实际同时承载 emoji 原色、DOM 文本、PAG/WebP 前景特效和跑马灯。文章复盘为什么会出现重影、闪烁、金色文本和 clone 重叠,并整理真实文本层与视觉特效层的分工、测量和测试护栏。2026-07-06
SVG 图标为什么在真机上被挤扁PC 模拟移动端正常,真机上返回、关闭、充值图标却被挤扁。文章从 Figma 导出的 SVG frame、viewBox、preserveAspectRatio、真实可见边界和 CSS 尺寸入手,说明为什么不能只看外层尺寸,也不要把 SVG 变形误判成 rem 或响应式问题。2026-07-06