ch单位宽度由当前font-family、font-size及浏览器字体度量实现共同决定,不同系统和引擎下0的渲染宽度存在亚像素差异。

ch 单位在部分浏览器中宽度显示不一致,根本原因不是它“不可靠”,而是你把它当成了“字符数单位”来用——它只保证和数字 0 的**当前字体渲染宽度**一致,而这个宽度在不同浏览器、不同字体引擎(如 Windows 的 GDI vs macOS 的 Core Text)、甚至同一浏览器开启/关闭字体平滑时都可能有亚像素级差异。
ch 的宽度到底由什么决定?
它依赖三个不可控变量:当前元素的 font-family、font-size、以及浏览器底层字体度量(font metrics)的实现。比如:
- Windows 上的
Segoe UI渲染0的宽度,在 Chrome 和 Edge 中可能差 0.2px; - macOS Safari 对等宽字体中
0的度量更保守,而 Firefox 可能计入 hinting 偏移; - 如果父级用了
font-feature-settings: "tnum"(表格数字),0的字形宽度可能被拉伸或压缩。
这些差异在小尺寸(如 width: 4ch)下尤为明显,肉眼就能看出对不齐。
em 和 rem 真的更稳定?
不一定。它们只是换了一种不稳定方式:
-
em是相对于父元素font-size,嵌套深了极易失控; -
rem虽然锚定根字号,但若项目用了动态根字号(如根据视口缩放),1rem就不再是固定物理宽度; - 两者都仍受字体度量影响——
1em= 当前字体的font-size,但“一个字符占多少em”依然没定义,只是比例尺变了。
换句话说:ch 不准是“绝对宽度不准”,em/rem 不准是“相对基准漂移”。想靠换单位解决一致性问题,本质是回避了问题根源。
真正可控的替代方案有哪些?
如果你要的是“刚好塞下 N 个英文字符”的效果,别依赖任何字体相关单位:
- 用
monospace字体 +ch,并显式锁定字体栈:font-family: ui-monospace, SFMono-Regular, 'Segoe UI Mono', 'Roboto Mono', monospace;; - 对关键容器加
text-rendering: optimizeLegibility;(Firefox/Safari 更倾向启用字体微调); - 需要像素级精确时,直接用
px+ JS 动态测宽:getComputedStyle(el).fontFamily拿到实际生效字体,再用canvas.measureText('0').width反查真实ch值; - 纯布局场景(如表单字段宽度),改用
min-width+ch组合:min-width: 8ch; width: fit-content;,让内容撑开而非硬卡。
为什么你看到的“不一致”常出现在 input 或 textarea?
因为表单控件默认字体不可控,且各浏览器内置样式差异极大:
- Chrome 在 Windows 下给
input强制设font: -webkit-small-control;,ch实际按系统控件字体算; - Safari 对
textarea的line-height和padding处理会间接影响ch的视觉密度; - 哪怕写了
font-family: monospace,Firefox 仍可能 fallback 到DejaVu Sans Mono,而 Chrome 用Cascadia Code—— 它们0的宽度就是不一样。
这种差异不是 bug,是浏览器尊重系统字体策略的结果。强行用 em 替代,只是把问题从“宽度不准”转移到“行高错位”或“光标偏移”上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











