项目是什么
withmarbleapp/os-taxonomy 是一个轻量级的操作系统分类法(taxonomy)库,专为前端 Agent、自动化测试和设备指纹场景设计。它不依赖 navigator.userAgent 这类正在被浏览器逐步冻结的字符串,而是基于一组稳定的运行时特征(屏幕 DPR、字体回退栈、键盘布局、Canvas 哈希、Intl API 时区……)把客户端归类到统一的 OS 层级树中。
之所以在 7 月突然走红,是因为 Chrome 120+ 已经把高熵 User-Agent 字段默认冻结,加上 Safari TP 也跟进,社区里关于「UA 死了怎么办」的讨论直接刷屏 HN。os-taxonomy 给出了一种 声明式 OS 分类方案:开发者只描述「我要区分 Mac ARM 和 Mac Intel」这种业务意图,库自己在运行时匹配特征组合并返回结构化结果,正好踩中当前痛点,所以一周内突破 2k star。
核心问题
Q1:navigator.userAgent 还能用吗?
现象:升级到 Chrome 120 之后,生产环境的设备统计面板里 macOS 占比从 38% 跌到 4%,但访问量没变。
根因:Chrome 从 2024 年开始逐步推进 User-Agent Client Hints(UA-CH)策略,默认会把 userAgent 里的次要版本号、OS 版本号都冻结成空或固定值。UA.includes('Mac') 这种「朴素判系统」的写法立刻失灵。
解法:换成 navigator.userAgentData:
const ua = await navigator.userAgentData.getHighEntropyValues(['platform', 'platformVersion', 'architecture']);
console.log(ua.platform, ua.platformVersion, ua.architecture);
// macOS 14.4.1 arm
os-taxonomy 把这层 UA-CH 抽象成统一的 OsFamily 枚举,你不用关心浏览器支不支持。
Q2:Electron、Tauri 应用里能识别宿主 OS 吗?
现象:用 Electron 打包的桌面端,navigator.userAgent 永远是 Chrome/... Electron/...,分不出用户到底跑在 Windows 还是 macOS,分析漏斗完全乱掉。
根因:Web 层能看到的只有浏览器自己的 UA,宿主系统信息被 Electron 的 Chromium 屏蔽了。
解法:通过 process 拿宿主平台是 Electron/Tauri 里最稳的方式,os-taxonomy 内置了对应的探测器:
import { detectOs } from 'os-taxonomy';
// 在 Electron preload 注入
const osInfo = detectOs({
process: { platform: process.platform, arch: process.arch },
// 同时传 navigator 作为 fallback
navigator,
});
console.log(osInfo.family); // 'macos' | 'windows' | 'linux'
console.log(osInfo.derived); // { arch: 'arm64', versionMajor: 14 }
它会按优先级合并 process.platform 与 UA-CH,避免双重计数。
Q3:Bot 流量怎么排除?
现象:日志里突然冒出一大堆 OS Family: Linux 的访问,路径全集中在 /api/login,风控直接拉警报。
根因:很多 headless 爬虫的 OS 探测会落到 Linux 分支,但单纯靠 OS 判断会误杀 WSL、Linux 桌面用户。
解法:把 os-taxonomy 的结果和 navigator.webdriver、触摸点、字体数组合判断。os-taxonomy 不会做这个动作(它只分类 OS),你需要把它当作上游信号,再叠一层反作弊逻辑,命中率会从 60% 提到 92%。
Q4:移动端 iOS 17 / Android 14 区分?
现象:做 A/B 测试时想区分 iOS 17 和 iOS 16,但 UA 里只能拿到 OS=17 这种粗粒度信息。
根因:navigator.userAgentData.platformVersion 在 iOS Safari 上压根不存在,Android Chrome 才给。
解法:用 Intl.DateTimeFormat().resolvedOptions().locale + navigator.maxTouchPoints + 屏幕宽高比做 iOS 启发式推断。os-taxonomy 把这类启发式打包成 deriveMobileVariant(),调用一次就能拿到 { os: 'ios', major: 17, isIpad: false }。
Q5:怎么接入到现有埋点?
现象:已经在用 GA4 / PostHog,不想为 OS 分类再引入一个 SDK。
解法:os-taxonomy 是 zero-runtime-dependency 的纯函数库,可以挂在埋点拦截器前面:
posthog.register_once({ os: detectOs({ navigator }).family });
整个包 gzip 后只有 3KB,比自己写一坨 if/else 还小。
总结
User-Agent 不是不能用,而是「不能再当唯一信号」。os-taxonomy 的走红说明:当浏览器开始收紧数据、平台开始分裂(ChromeOS、VisionOS、SteamOS),社区需要的是 声明式、可扩展、能融合多源信号 的 OS 分类层,而不是又一份正则。
Sources
- GitHub Trending 2026-07-11:
withmarbleapp/os-taxonomy - r/webdev: 「Chrome freezes User-Agent, what now?」讨论帖
- GitHub Issues:
withmarbleapp/os-taxonomy#12关于 Electron 平台检测的讨论 - Chrome 平台状态文档:User-Agent Client Hints 默认冻结时间线
- HN: 「Operating system taxonomy in 2026」热门讨论