camelCase vs snake_case vs kebab-case 命名约定完全指南 2026
userID 还是 userId?user_profile 还是 userProfile?URL 用 - 还是 _?这些问题在 PR review 里被反复争论,但答案并不取决于个人偏好——每种主流语言、每条 Web 标准都有定型的规则。
本文覆盖代码里会遇到的 6 种 case(camelCase、PascalCase、snake_case、kebab-case、CONSTANT_CASE、dot.case / path/case / Header-Case)、一份 7 语言决策矩阵、带 GitHub 数据的 parseUrl-vs-parseURL 缩写之争、kebab-case URL 的 SEO 理由,以及自动转换时常见的 6 个坑。需要看任意字符串在 15 种 case 下的输出,大小写转换工具能在浏览器里实时渲染。
6 种 case 一张速查
先把速查表放在最前面,方便对照。
| Case 风格 | 示例 | 典型用途 | 起源 / 推广者 |
|---|---|---|---|
| camelCase | userProfileImage | JS、TS、Java、Swift 的变量与方法 | Smalltalk → Java |
| PascalCase | UserProfileImage | 类、React/Vue 组件、TS 类型 | Pascal 语言 |
| snake_case | user_profile_image | Python、Ruby、Rust、SQL 列名 | C / 早期 Unix |
| kebab-case | user-profile-image | CSS 类名、URL slug、HTML 属性 | Lisp / 现代 Web |
| CONSTANT_CASE | USER_PROFILE_IMAGE | 环境变量、顶层常量、宏 | C 宏 / Unix 环境 |
| dot.case | user.profile.image | Java 包、MongoDB 路径、TOML 键 | 命名空间约定 |
| path/case | user/profile/image | URL 路径、文件系统、Git 引用 | Unix 路径 |
| Header-Case | User-Profile-Image | HTTP/1.1 头部名(canonical 写法) | RFC 2616 |
表里 8 行对应 6 种实质不同的 case。dot.case、path/case、Header-Case 共用同一套分词规则,只是分隔符不同,大多数 case 库会把它们归为同一家族。
每种 case 的细节
camelCase:JS/Java 的默认
camelCase 由 Smalltalk 提出,Java 把它推向了全行业。Sun 1995 年的 Java 编码规范把 firstName、getUserProfile、xmlParser 钉为默认写法,此后追随 Java 风格的语言(JavaScript、ActionScript、Swift、Kotlin、Dart)都继承了同一形态。
规则:首词全小写,后续每个单词首字母大写,分隔符全部去掉。没有下划线,没有连字符,没有空格。名字源自大写字母在小写中凸起的驼峰外形。
两个常出错的边缘情况。一是品牌名里以小写起首的词(iPhone、eBay、iOS),出现在代码里时按品牌原写法保留,不要强行把 i 大写。二是缩写,下面会专门讨论。
PascalCase:类与组件
PascalCase 是首字母大写版的 camelCase。有些风格指南叫它「UpperCamelCase」。Pascal 语言 1970 年代就在用,名字由此而来。
适用范围:所有 C 系 OO 语言的类名(Java、C#、C++、Kotlin、Swift、TypeScript)、React/Vue/Angular 的组件名、TypeScript 的类型别名与接口(type UserProfile、interface AuthState),以及某些生态里的模块/文件名(C# 的 UserService.cs)。
为什么要单独给类一种 case?为的是视觉信号。new userProfile() 和 new UserProfile() 摆在一起,后者一眼就是类型。在值与类型共享命名空间的语言里,大小写承担了消歧的工作。
snake_case:Python、Ruby、Rust、SQL
snake_case 比一般印象更古老。C 和早期 Unix 就在用 errno_h、fopen_s 这类名字,PDP-11 终端键盘按下划线方便,而 Pascal 那种大小写来回切的写法很费劲。Python 把它写进 PEP 8 作为官方约定;Ruby 社区自发收敛到 snake_case;Rust 把它做成编译器强制的默认,写成 userId 而不是 user_id,lint 立刻报错。
规则:全小写,单词之间用下划线连接。user_profile_image、parse_html、max_retries。
数据库这一面很关键,多数语言教程会跳过。几乎所有 SQL ORM(SQLAlchemy、Hibernate、Sequelize、TypeORM、Active Record)默认都用 snake_case 列名,不管宿主语言的约定是什么。原因是可移植性:PostgreSQL 把未加引号的标识符折叠成小写,MySQL 在 Linux 上区分大小写、在 macOS/Windows 上不区分,SQLite 把列名当成不透明字符串。snake_case 是唯一不加引号就能在所有数据库里行为一致的写法。
kebab-case:Web 的选择
Web 在所有用户可见的地方都汇聚到了 kebab-case:CSS 类名(.user-profile-image)、URL slug(/blog/naming-conventions-guide)、HTML 自定义属性(data-user-id)、Web Component 标签名(<user-card>,规范要求里面必须含连字符)。
这个名字在旧文档里有过多种叫法:「dash-case」「spinal-case」「lisp-case」「skewer-case」「hyphen-case」。指的都是同一种东西,「kebab-case」是后来通用的名字。
一个不显眼的规则:HTML 和 CSS 的类名行为上不区分大小写,但 canonical 写法是全小写。.User-Profile 在大多数浏览器里能用,但会让对类名做哈希的服务端工具出问题。坚持全小写就对了。
CONSTANT_CASE:环境变量与宏
CONSTANT_CASE(Rust 圈子里有时叫 SCREAMING_SNAKE_CASE)是「这个值在运行时永远不变」的通用信号。MAX_RETRIES、API_KEY、DEFAULT_TIMEOUT_MS。各语言都有对应约定;CI 系统、容器运行时、shell 都期望环境变量是这种写法(DATABASE_URL、NODE_ENV、PATH)。
陷阱:JavaScript 的 const 关键字并不意味着「该用 CONSTANT_CASE」。const result = await fetch(url) 是正确的 camelCase。CONSTANT_CASE 留给语义上的常量——在 C 里会用 #define 写出来的那种、运行时改值就是 bug 的东西。MAX_RETRIES = 3 符合标准,result 不符合。
dot.case、path/case、Header-Case
三种共享同一分词逻辑、仅分隔符不同的变体。
dot.case 表达层级键:Java 包名(com.example.service)、MongoDB 字段路径(user.profile.image)、TOML/INI 配置键([database.primary])、Lodash 方法路径(_.get(obj, 'user.profile.image'))。一个 dot.case 字符串读出来的结构是「命名空间,命名空间,叶子」。
path/case 表达真实位置:URL 路径、文件系统路径、Git 引用(feature/add-auth)。点和斜杠的选择是有意义的:斜杠表示某处真实存在的东西,点表示一个标签。
Header-Case 是 HTTP/1.1 的约定:Content-Type、Access-Control-Allow-Origin、X-Forwarded-For。HTTP/1.1 头部在协议层不区分大小写(RFC 2616),content-type 也能工作,但框架、文档和开发者习惯都期望 Header-Case 写法。HTTP/2 和 HTTP/3 改变了这一点:RFC 7540 §8.1.2 强制要求在线传输的头部名全小写,目的是简化头部压缩(HPACK)。这对应用代码是透明的,HTTP/2 客户端和服务器会自动归一化;如果直接抓一个 HTTP/2 帧,里面的头部是全小写的 kebab-case。
7 语言决策矩阵
平息命名争论最快的办法,是去看这门语言的标准库怎么写。矩阵如下。
| 语言 | 变量 | 函数 | 类 | 常量 | 文件名 | DB 列名 |
|---|---|---|---|---|---|---|
| Python (PEP 8) | snake_case | snake_case | PascalCase | CONSTANT_CASE | snake_case.py | snake_case |
| JavaScript/TS | camelCase | camelCase | PascalCase | CONSTANT_CASE | kebab-case.js | snake_case |
| Go | camelCase* | PascalCase** | PascalCase | mixedCase*** | snake_case.go | snake_case |
| Rust | snake_case | snake_case | PascalCase | SCREAMING_SNAKE | snake_case.rs | snake_case |
| Java | camelCase | camelCase | PascalCase | CONSTANT_CASE | PascalCase.java | snake_case |
| C# | camelCase† | PascalCase | PascalCase | PascalCase | PascalCase.cs | snake_case |
| SQL | n/a | snake_case | n/a | n/a | n/a | snake_case |
*Go:首字母小写表示未导出(包级私有),首字母大写表示导出(公开)。编译器强制这一约定。**Go:导出函数用 PascalCase(http.NewRequest),包级私有函数用 camelCase(http.parseHeader)。***Go:常量遵循同样的导出/未导出规则——导出常量写作MaxRetries,未导出写作maxRetries。Go 刻意避开了 CONSTANT_CASE。†C#:局部变量和私有字段是 camelCase(部分代码库会给字段加_前缀:_userName);公开属性、方法、类型是 PascalCase。
三个跨语言、横切所有栈的层:
HTML 与 CSS:类名和 ID 是 kebab-case(<div class="user-profile-card">)。HTML 自定义属性是带 data- 前缀的 kebab-case(data-user-id)。内联 CSS 属性是 kebab-case(background-color);对应的 JS DOM 写法是 camelCase(element.style.backgroundColor)。
HTTP:在 HTTP/1.1 上发出的头部名是 Header-Case('Content-Type': 'application/json'),在 HTTP/2 在线传输时是小写 kebab-case。大多数 fetch 库都能接受任一写法,内部会自动归一化。
环境变量:全部 CONSTANT_CASE——Node、Python、Go、Rust、Bash、Docker、Kubernetes 一视同仁。.env 文件也是同样的约定:DATABASE_URL=postgres://...。
缩写处理:Google vs Microsoft
这是 code review 里争议最大的命名问题。该写 parseUrl 还是 parseURL?userId 还是 userID?HtmlParser 还是 HTMLParser?XmlHttpRequest 还是 XMLHttpRequest?
有两套流派,背后都有权威背书。
把缩写当作普通词(Google、Apple、现代 JS):parseUrl、userId、HtmlParser。Google JavaScript Style Guide §5.3 明文推荐,Apple Swift API Design Guidelines 同样如此。lodash 和 change-case 包默认产出这种写法。核心理由是往返稳定性:parseUrl 能干净地分词为 parse / url,转成 parse_url,再转回 parseUrl,信息无损。parseURL 被分词为 parse / URL,朴素分词器输出 parse_u_r_l,缩写感知的分词器输出 parse_url;但 parse_url 无法判断该回到 parseUrl 还是 parseURL,全小写写法把缩写信号弄丢了。
保留缩写大写(Microsoft、.NET、老式 Java):parseURL、userID、HTMLParser、XMLHttpRequest。Microsoft .NET Naming Guidelines 把这条规则限制在 2-3 字母的缩写(IO、URL、XML),更长的缩写按普通词处理(按严格规则 Html 该保留大写,但 Microsoft 实际写的是 HtmlAgilityPack)。Win32 API、.NET BCL 以及 2010 年以前的大多数 Java 代码都用这种风格。它对英语读者更直观(parseURL 看上去就是「parse U-R-L」),代价是失去往返性。
Python 的 PEP 8 名义上推荐当作普通词,但标准库历史上并不一致:http.server.HTTPServer、xml.etree.ElementTree、json.JSONDecoder 保留了缩写大写;新近加入的 pathlib.PurePath、dataclasses 倾向于当作普通词。PEP 8 的原则是跟随周围代码的风格。
2026 年初对 GitHub 公开语料的抽样(BigQuery 的 bigquery-public-data.github_repos,筛选 star 数 1k+ 仓库的 TypeScript 与 JavaScript 文件)显示 parseUrl 与 parseURL 约为 7:3,userId 与 userID 约为 6:4。普通词风格在 JavaScript 里占上风。C# 仍然倾向 Microsoft 风格,parseURL 在 C# 文件里占主导。Python 大致五五开。
决策规则:(a) 跟随所写语言的标准库;(b) 标准库本身不一致时,新项目选普通词风格,因为它能往返;(c) 把决定写进 lint 配置或风格指南,单个项目内不混用。大小写转换的分词器采用普通词约定,与 lodash 和 change-case 一致:粘贴 XMLHttpRequest,会得到 xmlHttpRequest、xml_http_request、xml-http-request 作为 camelCase、snake_case、kebab-case 的输出。
URL Slug:为什么 kebab-case 击败 snake_case
Google Search Central 关于 URL 结构的官方文档有一条具体的 case 建议:URL 里用连字符分隔单词,不用下划线。原因是分词。Google 搜索索引在连字符处切分 URL,不在下划线处切。https://example.com/buy-running-shoes 被分词为 buy、running、shoes 三个可索引词项,任意一词命中都算。https://example.com/buy_running_shoes 则被分词为单一词项 buy_running_shoes,只有一字不差的查询才会命中。
对排名稳定的页面,差距很小(Google 还有别的信号);对在拥挤 SERP 里争位置的新页面,差距真实存在。同等条件下,kebab-case URL 排名更高。
第二个原因是大小写敏感性。URL 路径在 Linux 服务器上区分大小写(也就是绝大多数 Web 环境)。/User-Profile 和 /user-profile 是两个不同的 URL、两条不同的缓存记录、两行不同的分析数据。小写 kebab-case 是唯一不会引来跨环境 bug 的写法。
任何标题都能套用的 4 步 slug 配方:
- 全部小写。
- 把连续空白和标点替换成单个连字符。
- 去掉首尾的连字符。
- 可选:去掉停用词(
a、an、the、of、for)以缩短 URL。仅在 CMS 会保留原始标题作为页面 H1 时才这么做。
实战示例:"10 Tips for Faster JavaScript: A Complete Guide" → 10-tips-faster-javascript-complete-guide。冒号和停用词(for、a)被剥掉;结果 39 个字符,落在 50-60 字符的 SERP 显示区间内。关于 URL 长度与各平台字符限制的互动,参考字符与字数限制完全指南。
大小写转换能把任意标题一键转成 kebab-case,给 CMS 迁移或 sitemap 批量生成 slug 时很省力。
6 大转换陷阱
case 之间的自动转换看起来简单,实际并不。下面是常出问题的 6 个地方。
1. 数字与字母的边界
file2x 经 snake_case 转换后是什么?主流约定(lodash、change-case、PEP 8、大小写转换)把每一次字母↔数字过渡都当作 token 边界,所以 file2x 被切成 file / 2 / x,snake_case 化后是 file_2_x。parseUTF8 被切成 parse / utf / 8,结果是 parse_utf_8。
某些老库以及 Stack Overflow 上的手写 re.sub 片段会跳过这条规则,把 file2x 原样保留,把 parseutf8 拼在一起。这种不匹配只在跨库迁移代码时浮出水面,症状是「一半标识符被改名、另一半没改」。挑一个分词器,确认它遵守数字边界规则,然后一直用它。
2. 连续的大写字母
缩写边界的正则是 /([A-Z]+)([A-Z][a-z])/,在「一段大写」和「一个大写起首新词」之间切开。XMLHttpRequest 匹配成 XML + HttpRequest,再拆 Http + Request,最终得到 token XML / Http / Request。
反向回去时问题就来了:XML / Http / Request 再 PascalCase 化得到 XmlHttpRequest,不是 XMLHttpRequest,缩写被 title-case 化了。这是标准行为,另一种做法需要分词器没有的带外元数据来记住哪些 token 原本是缩写。如果代码库使用 XMLHttpRequest 风格,又用一个普通词转换器在项目范围内做改名,每一个缩写都会被悄无声息地重写。先在分支上跑测试,或者用一个允许显式标记缩写的分词器。
3. Unicode 与 locale 感知的大小写映射
JavaScript 里 'I'.toLowerCase() 通常返回 'i'。在土耳其 locale 激活时同样的调用返回 'ı'(无点 i,U+0131),因为土耳其语有两个 i 字母,大写 I 的小写形式正是无点的那一个。这个 bug 在国际化上线时反复出现:登录表单把用户名转大写后比对,土耳其用户 İrem 永远登不进自己的账号。
还有两个雷区。德语的 ß.toUpperCase() 返回 'SS',一个字符变成两个,任何假定 case 转换保留字符串长度的代码都会出错。希腊语 Σ.toLowerCase() 是上下文相关的:词中是 σ,词末是 ς。
修复:用带显式 locale 的 toLocaleLowerCase() 和 toLocaleUpperCase();不知道用户 locale 时传 'en-US' 拿到 ASCII 兼容的行为。大小写转换使用 Intl 感知的方法,能正确处理上述三个输入。正则那一面,正则表达式完全指南讲了 \p{L} 这个 Unicode 字母类。
4. 智能引号污染
从 Microsoft Word、Google Docs 或 macOS 备忘录粘贴字符串到 case 转换器里,可能携带一批隐形字符:U+2018 / U+2019 / U+201C / U+201D(弯引号)、U+2014(长破折号)、U+00A0(不间断空格)、U+200B(零宽空格)。这些字符在多数字体里看起来与 ASCII 版本相同,编码却不同。包含 U+00A0 的 camelCase 标识符在某些语言能编译、另一些不能;用 grep 搜变量名时也会漏掉。
防御:分词前先归一化输入。一行 input.normalize('NFKC').replace(/[“”‘’]/g, '"') 能处理掉大部分。或者参考文本对比在线工具指南的做法,把可疑字符串和它的 ASCII 形态做 diff,在 hex 视图里定位隐形字符。
5. URL 不该被 snake_case 化
https://example.com/api/users 粘进 snake_case 转换器会变成 https_example_com_api_users。技术上是合法的 snake_case 标识符,语义上没有意义。URL 已经处在 canonical case 上(path/case,路径段是小写 kebab-case),把整个 URL 当作单一标识符处理会丢掉结构信息。
修复办法是先解析 URL,提取路径段,需要时对每段单独转换。大小写转换刻意不自动解析 URL:猜测意图比字面处理更危险。粘 URL 就字面转换,需要分段处理就自行分段。
6. dot.case 与属性访问
字符串 user.profile.image 在不同上下文里是两种不同的东西。在 TOML 文件里作为 dot.case 标识符,它是一个由三段组成的名字。作为 JavaScript 表达式,它是 user 的 profile 属性的 image 属性。
把 dot.case 字符串从配置文件复制到 JavaScript 控制台,运行时会把它当属性链求值,要么报错,要么返回意外结果。反过来,处理 JS 属性路径的代码('a.b.c'.split('.'))有时也会处理来自配置的 dot.case 标识符,把它当成比预期更深的路径。两者必须保持命名空间分离。
约定:dot.case 字符串只在数据里出现(配置文件、MongoDB 路径、日志键);单一标识符的代码用 camelCase 或 snake_case;代码里需要层级结构时,用嵌套对象加宿主语言的点属性语法。
跨语言迁移配方
JS camelCase 转 Python snake_case
最快的流程是复制 JS 标识符,粘到转换器里,复制 snake_case 输出。批量做代码级转换:
import { snakeCase } from 'change-case';
snakeCase('parseHTML'); // 'parse_html'
snakeCase('XMLHttpRequest'); // 'xml_http_request'
snakeCase('parseUTF8'); // 'parse_utf_8'
snakeCase('iPhone'); // 'i_phone'
最后一个是坑。iPhone 是品牌名,camelCase 边界误导了分词器。品牌名以及少数历史标识符,转换后要手动修一遍。
SQL snake_case 转 JS/Java API 响应
绝大多数 ORM 会自动处理。Sequelize 有 underscored: true,TypeORM 有 SnakeNamingStrategy 类,Hibernate 有 ImplicitNamingStrategyComponentPathImpl。默认映射是 user_profile_id ↔ userProfileId。
会出问题的地方是带缩写的列。名为 http_status_code 的列能干净地往返为 httpStatusCode,但代码库偏好 HTTPStatusCode 时 ORM 会和它打架。要么把列改成 httpstatuscode_code(不好看),要么配置 ORM 保留缩写(少见),要么接受标准约定。
React PascalCase 组件转 CSS kebab-case 类名
// UserProfileCard.tsx
export function UserProfileCard({ user }) {
return <div className="user-profile-card">{user.name}</div>;
}
/* UserProfileCard.module.css */
.user-profile-card { padding: 1rem; }
.user-profile-card__avatar { border-radius: 50%; }
.user-profile-card--featured { background: gold; }
BEM(Block Element Modifier)是和 React 最常配对的 CSS 类名约定:block 是 kebab-case 的组件名,element 是 block__element,modifier 是 block--modifier。文件层面:组件用 UserProfileCard.tsx,作用域样式用 UserProfileCard.module.css,两者都是 PascalCase,与组件同名。
环境变量到应用配置
# .env (CONSTANT_CASE)
DATABASE_URL=postgres://localhost/myapp
MAX_RETRIES=3
LOG_LEVEL=info
// Node.js
const dbUrl = process.env.DATABASE_URL;
const maxRetries = parseInt(process.env.MAX_RETRIES, 10);
# Python
import os
db_url = os.environ['DATABASE_URL']
max_retries = int(os.environ['MAX_RETRIES'])
环境变量名保持 CONSTANT_CASE,应用侧的标识符跟随该语言的变量约定。YAML/TOML 配置键按惯例是 snake_case(database_url、max_retries),即便运行时映射到同一批 CONSTANT_CASE 环境变量。Spring、dotenv、Pydantic 这类框架会处理 case 映射。
库与工具对比
| 工具 | 语言 | 支持的 case | 分词器行为 |
|---|---|---|---|
lodash(_.camelCase 等) | JavaScript | 4 种主流 + startCase | 缩写当作普通词 |
change-case npm 包 | JavaScript/TS | 全部 8 种编程 case | 缩写当作普通词 |
inflection(Python) | Python | camelCase / snake_case | 缩写当作普通词 |
convert_case crate | Rust | 12+ 种 case | 可配置缩写处理 |
Go strings + 正则 | Go | 手写实现 | 项目自定义 |
| VS Code(内置) | 编辑器 | 仅 UPPER / lower / Title | 只按空白分 |
| VS Code「change-case」扩展 | 编辑器 | 全部 8 种编程 case | 缩写当作普通词 |
| 大小写转换 | 浏览器 | 15 种 case(7 文本 + 8 代码) | 缩写当作普通词 |
日常代码开发装 change-case(JS)或 convert_case(Rust)。Python 用 inflection 是经典选择,一段简短的手写正则也能覆盖 90% 的场景。code review 或重构时的零散转换,大小写转换在一次粘贴里展示全部 15 种输出,方便对比。需要统计 token 数或验证标识符长度时用字数统计;验证分词正则可以用正则表达式测试配合上文链接里的速查表模式。
FAQ
camelCase 和 PascalCase 有什么区别?
camelCase 以小写字母开头(userProfile),PascalCase 以大写字母开头(UserProfile)。两者后续单词首字母都大写,且无分隔符。camelCase 是大多数 C 系语言里变量与函数的约定,PascalCase 用于类、类型和 React 组件。
为什么 Python 用 snake_case,JavaScript 却用 camelCase?
Python(1991)从 C 和 ABC 语言继承了 snake_case,PEP 8 把它定为社区标准。JavaScript(1995)沿用了 Java 的 camelCase 风格,Java 又承袭自 Smalltalk。两者都是历史路径依赖。哪种约定都谈不上更优(可读性研究的结果大致打平),生态内部的一致性比选哪种更重要。
camelCase 里的缩写该写 parseUrl 还是 parseURL?
parseUrl(把缩写当作普通词)是现代默认,Google、Apple、lodash、change-case npm 包都这么用。parseURL(保留缩写大写)是 Microsoft .NET 风格,在 C# 代码里占主导。新建 JavaScript、TypeScript 或 Swift 项目选 parseUrl,因为它能在 snake_case 和 kebab-case 之间干净地往返。无论选哪种,都要写进 lint。
URL 用 kebab-case 比 snake_case 好吗?
是的。Google Search Central 的官方建议是 URL 里用连字符,不用下划线。搜索索引在连字符处切词,不在下划线处切:/user-profile 被索引为 user + profile,/user_profile 被索引为单一词项 user_profile。每页的排名影响小但真实存在,小写 kebab-case URL 还能避免 Linux 服务器上的大小写敏感性 bug。
数据库列名应该用什么 case?
snake_case。主流 ORM(SQLAlchemy、Hibernate、Sequelize、TypeORM、Active Record)默认用它,主流 SQL 方言对它的处理一致。PostgreSQL 把未加引号的标识符折叠成小写,MySQL 在 Linux 区分大小写、在 macOS/Windows 不区分,SQLite 把名字当作不透明字符串。小写 snake_case 是在所有环境下行为一致的写法。
一个项目里可以混用多种命名约定吗?
可以,而且通常必须如此。一个典型 Web 应用会在 JS 变量用 camelCase、数据库用 snake_case、CSS 类与 URL 用 kebab-case、环境变量用 CONSTANT_CASE。规则是一层一种约定,单层之内不混用。把每一层的选择写进 lint 或风格指南,PR review 不会再为它浪费一个字。
如何用程序在 case 之间转换?
JavaScript 和 TypeScript 装 change-case,或用 lodash 的 _.camelCase / _.snakeCase / _.kebabCase。Python 用 inflection 包,或一段短正则(PascalCase 转 snake_case 是 re.sub(r'(?<!^)(?=[A-Z])', '_', s).lower())。Rust 用 convert_case crate。一次性交互转换用大小写转换,在浏览器单页里展示任意输入的 15 种 case 输出。
CONSTANT_CASE 只用于环境变量吗?
不是,但环境变量是最常见的用法。CONSTANT_CASE 用于任何运行时不变量:MAX_RETRIES、API_BASE_URL、DEFAULT_PAGE_SIZE、enum 值、宏定义、顶层配置常量。判断标准是「运行时改这个值算不算 bug」:算就用 CONSTANT_CASE,否则用语言里普通的变量约定。const result = await fetch(url) 保持原样即可。
dot.case 和 path/case 有什么区别?
dot.case 用 . 作分隔符(user.profile.image),表达数据内部的层级键:Java 包、MongoDB 字段路径、TOML 配置键、Lodash get/set 路径。path/case 用 /(user/profile/image),表达真实位置:URL 路径、文件系统路径、Git 引用。点和斜杠的差别在于数据标签与实际位置的语义。
30 秒决策清单
三条规则就能覆盖 95% 的问题:
-
写代码时跟着所在语言的标准库。Python:变量与函数 snake_case,类 PascalCase。JavaScript 和 TypeScript:变量与函数 camelCase,类与组件 PascalCase。Go:包级私有用小写起首,导出用大写起首。Rust:变量与函数 snake_case,类型 PascalCase,常量 SCREAMING_SNAKE。
-
跨语言的横切层,case 是固定的。URL 永远 kebab-case。CSS 类名永远 kebab-case。数据库列名永远 snake_case。环境变量永远 CONSTANT_CASE。HTTP/1.1 头部永远 Header-Case(HTTP/2 在传输层会归一化为小写)。
-
把选择写进 lint,一次性结束争论。ESLint、Pylint、Clippy、golangci-lint、Rubocop 都有对应规则。选好约定、配好 lint,下一次 PR review 就不会再为
userID还是userId多花一个字。
需要在 case 之间转换(重构、CMS 迁移、SQL 到 API 的映射)时,大小写转换能在一次粘贴里给出全部 15 种输出,复制需要的那个即可,不必手工分词。其他文本工具见字数统计、正则表达式测试和文本对比在线工具。深入阅读:正则表达式完全指南讲分词器需要的模式,文本对比在线工具指南讲迁移过程中的前后对比,字符与字数限制完全指南讲 SEO slug 的 URL 长度预算。