背景:
最近因为项目构建速度过慢,调试速度过慢,把100万行的Vue2项目升级到Vue3 + Vite了,顺手把Element UI升到Plus, Vxe什么都升级了。结果所有其他bug都修完后,测试报来一个神奇的问题, 而且居然是偶发的:
预期:
实际结果
分析过程
到测试电脑上点进对应style看看:
.vxe-table-icon-checkbox-unchecked:before {
content: "";// 乱码
}
刷新后再打开同个站点却是:
.vxe-table-icon-checkbox-unchecked::before {
content: ""; // 正常了
}
回到自己的工位上面, 尝试通过页面GET访问这个css文件,第一次:
.vxe-table-icon-checkbox-unchecked:before{
content:"顪�" // 中文乱码,GB2312无疑
}
刷新后:
.vxe-table-icon-checkbox-unchecked:before {
content: "";// 乱码
}
再刷新几次就在这两个编码格式之间随机出现。
难道css文件还能不一样的?确认了一下,对应css文件的hash完全一致。都是app.84091779.css,并且如果是不同文件,怎么可能是偶发bug。
从docker里面把对应css文件下载下来,本地CURSOR试着改了改编码测了一下,发现测试的主机上偶发的编码格式是windows1252,刷新后的编码是utf-8。
看了一下对应的CSS文件的Response头
content-type: text/css
并没有指定charset, 看起来确定是编码问题了。但是这里为什么是偶发的, 基于以前的经验以及AI的回答简单分析下
偶发原因
前提条件
- 无 Content-Type: charset=
- 无 @charset 规则
- 无 BOM
- 无 HTML 引用文档(直接访问 CSS)
此时浏览器走编码嗅探算法(Encoding Sniffer),而TCP 分包导致嗅探窗口不同,这才是问题的根源 编码嗅探器不会等整个文件下载完,它只扫描首次可用的字节块来做判断。
请求1: 首个 TCP 包包含字节 A~B → 嗅探器认为是 GB2312
请求2: 首个 TCP 包包含字节 A~C(多了一点)→ 嗅探器认为是 Windows-1252
每次请求的 TCP 分包边界不固定(受网络抖动、服务器 buffer 状态影响),导致嗅探器"看到的初始窗口"不同,输出结果不同。
GB2312 和 Windows-1252 为什么特别容易互相误判
这两个编码的字节范围有严重重叠:
| 字节范围 | GB2312 | Windows-1252 |
|---|---|---|
| 0x80–0x9F | 无效/不用 | 可打印字符(€ 等) |
| 0xA1–0xFE | 汉字双字节 | 高位拉丁字符 |
当 CSS 文件包含中文注释或中文内容(哪怕是 UTF-8 编码的,某些字节恰好落在这个范围),嗅探算法的统计模型看到不同长度的样本,可能得出不同结论。
最终方案
nginx css文件头配置里面添加charset=utf-8。
外记
毕竟是AI做的最终分析,还是需要真正去确认是否是浏览器嗅探机制。可以使用wireshark抓一下包
- 过滤 tcp && ip.addr == 你的服务器IP
- 对比两次请求,看首个数据包(PSH 标志)携带了多少字节
- 如果第一帧字节数不同,就能解释为什么嗅探结果不一致