首页
/ Electron 代码风格指南:多语言仓库的通用规范、工具链与命名约定

Electron 代码风格指南:多语言仓库的通用规范、工具链与命名约定

2026-09-06 18:34:39作者:滑思眉Philip

Electron 是一个横跨 C++(Chromium/Node.js 绑定层)、Objective-C++、Python(构建与补丁脚本)、TypeScript/JavaScript(lib/ 下的内置模块与默认应用)、Markdown(API 文档)的多语言大型仓库。为了让跨越几十个子系统的代码保持一致性,Electron 以 docs/development/coding-style.md 为总纲,制定了一套覆盖通用编码习惯、C++/Python 规范、文档写作、JavaScript 语法以及 API 命名约定的风格体系。本文以此为骨架,结合仓库中的实际脚本(script/lint.jspackage.json)、规则配置(.oxlintrc.json.markdownlint-cli2.jsonc)与 API 文档结构,给出可直接落地的规范清单与自检命令,帮助你提交符合 Electron 社区标准的改动。

一句话总览:风格是"规范文档 + 自动化工具"的结合

Electron 的风格要求并不是纸上谈兵——几乎每条规范都有对应的工具在 CI 或 pre-commit 阶段强制执行:

  • cpplint 负责 C++/Objective-C++ 风格检查;
  • oxlint 负责 JavaScript/TypeScript 的静态检查;
  • clang-format / oxfmt / gn format 分别负责 C++、JS/TS、GN 文件的格式化;
  • markdownlint-cli2lint-roller 系列插件 负责 Markdown 文档的排版与 API 文档结构校验。

仓库根目录的 package.json 中,一条 npm run lint 聚合了全部检查链路。文档中明确指出:直接运行 npm run lint,即可查看 cpplintoxlint 报告的各类风格问题。

通用代码规范:适用于所有语言的底线

coding-style.md 在开篇定义了与语言无关的通用规则,这是所有进入仓库代码的第一道关卡。

以换行符结尾

每个文件必须以一个换行符结尾。这避免了 POSIX 工具解析、diff 合并时出现"missing newline at end of file"问题,也让 git apply、补丁生成等场景更稳定(补丁检查逻辑见 script/lint.js 中对 .patch 文件的尾随空白检查)。

require/import 的排列顺序

CommonJS 时代的规定延续至今,要求 require 语句按以下三类分组排列:

  1. Node.js 内置模块(如 pathfs
  2. Electron 内置模块(如 ipcapp
  3. 本地模块(使用相对路径引用)

在类型检查层,仓库还进一步强制了现代写法:在 buildscriptdocsdefault_appspec 目录中开启 unicorn/prefer-node-protocol 规则(见 .oxlintrc.json),要求内置模块统一写成 node:fsnode:path 这类带协议前缀的形式,避免与第三方同名包混淆。script/lint.jsdocs 目录下代码块中的裸内置模块导入同样会报错(见 script/lint.js)。

类成员的排列顺序

类中成员按"静态优先、实例在后"的顺序组织:

  • 类方法与静态属性(即方法名前带有 @ 前缀的装饰器成员,或 static 成员)
  • 实例方法与实例属性

这与许多语言规范中"静态成员放在类顶部"的做法一致,便于读者在扫读类定义时先看到不依赖实例的入口。

避免平台相关代码

桌面应用天然要面对 macOS / Windows / Linux 的差异,规范明确要求两条硬性约定:

  • 拼接文件路径必须使用 path.join(),禁止手工拼 /\——这保证了在 Windows 上生成正确分隔符;
  • 需要引用临时目录时必须用 os.tmpdir()禁止硬编码 /tmp——因为 Windows 的临时目录是 %TEMP%,而 macOS 则可能是其他系统路径。

这两条同样适用于在 Markdown 文档、测试样例中演示 Node.js 代码的场景。

显式返回时使用裸 return

当需要在函数末尾显式结束流程时,只写 return,不要写 return nullreturn undefined,也不要直接返回 nullundefined。这一约定让"有意结束"与"无返回值"两种语义边界更清晰,也避免在类型推断时引入冗余的 null | undefined

C++ 与 Python:全面对齐 Chromium 上游风格

Electron 的 shell/ 目录(主进程 C++ 实现)直接依赖 Chromium 的大量抽象与类型,因此其 C++/Python 风格完全跟随 Chromium 的 Coding Style 规范(即 Chromium 官方 styleguide 所定义的缩进、命名、注释与所有权约定)。文档特别建议:若想深入理解 C++ 层的写法,应先熟悉 Chromium 的核心抽象与数据结构文档——其中介绍了一些贯穿 Electron 源码的特殊类型、作用域类型(在超出作用域时自动释放内存的 RAII 封装)、日志机制等。这些模式在 shell/browser/shell/common/shell/renderer/ 等目录中随处可见。

当前仓库约定的 Python 版本为 Python 3.9script/ 下大量构建脚本基于该版本,构建与补丁系统调用 python3 运行)。

在工具层面,文档提到的 script/cpplint.py 由统一入口驱动:script/lint.js 中 C++(.cc/.h)与 Objective-C++(.mm)两条 lint 分支实际执行的是

  • 先跑 script/run-clang-format.py(仓库根目录提供 .clang-format 配置)做格式化;
  • 再调用 depot_tools 中的 cpplint,并传入了经过裁剪的过滤器列表 CPPLINT_FILTERS(见 script/lint.js)。

被裁剪的检查项包括 -build/include-readability/casting、各类 -whitespace/*-runtime/* 等——它们之所以被关闭,是因为 Electron 的 C++ 层大量使用 Chromium 风格的非标准写法(如自定义的智能指针宏、原生类型别名),若全部开启会产生海量噪声。Objective-C++ 分支还会额外追加 -readability/braces 的放宽(见 script/lint.js)。

对开发者而言,常用的组合命令包括:

# 单独检查 C++(.cc/.h)
npm run lint:cpp

# 单独检查 Objective-C++(.mm)
npm run lint:objc

# 单独检查 Python 脚本(script/ 下 .py)
npm run lint:py

文档:用 markdownlint 与 lint-roller 保证可读性与一致性

Electron 把文档当作代码一样严格审查。规范要求所有文档正文遵循独立的文档风格指南,该指南细化了标题层级、大小写(页面标题用 Title Case,章节用 Sentence Case)、列表符号、代码块语言标识、API 文档固定模板(Methods/Events/Class:/Instance Methods/Instance Properties 等章节的书写格式)以及 API History YAML 块的存放规则。

仓库内可通过 npm run lint:docs 一键校验文档改动格式是否合规。在 package.json 中可以看到,这条命令是一个长长的流水线,依次执行:

  1. lint:js-in-markdown——用 lint-roller 的 markdown-standard 工具,对文档内所有 js/ts 代码块做 standard 风格 lint;
  2. create-typescript-definitions——重新生成 API 类型定义并跑 TS smoke 测试,确保文档与真实 API 对齐;
  3. lint:ts-check-js-in-markdown——对文档代码块做 TypeScript 检查;
  4. lint:docs-fiddles——对 docs/fiddles/ 下的示例做 standard 检查;
  5. lint:docs-relative-links——校验文档内部相对链接是否存在(防止死链,这也解释了为何文档链接必须以仓库根目录为基准正确书写);
  6. lint:markdownlint:api-history——前者基于 markdownlint-cli2.markdownlint-cli2.jsonc 的规则(继承自 @electron/lint-roller/configs/markdownlint.json)检查全部 .md;后者按 docs/api-history.schema.json 校验 API 变更记录块。

script/lint.js 中 Markdown 分支还有几条易踩的硬性约束(见 script/lint.js):

  • 代码块语言标识必须全小写;
  • 优先用 js/ts 作为标识符,而不是 javascript/typescript
  • 文档代码块若引用了 docs/fiddles/ 下的真实 fiddle 文件,其内容必须与源文件完全一致(防文档与示例脱节);
  • 代码块中的 Node 内置模块必须写 node: 前缀。

JavaScript / TypeScript:从 standard 到 oxlint 的演进

JavaScript 层(lib/default_app/npm/script/spec/ 等)要求编写 standard 风格的代码。早期这条规则由 ESLint + standard 插件承载;在本仓库当前版本中,主流检查已迁移到更快的 oxlint.oxlintrc.json),而 fiddles 与文档内嵌代码块仍通过 @electron/lint-roller 内置的 standard-markdown 保持对 standard 规范的兼容。

核心语法要求包括:

  • 文件命名:用 -(kebab-case)连接单词,如 file-name.js 而非 file_name.js。规则源于 Node.js 生态中模块名普遍采用 module-name 形式(如 npm 包名);该规则只约束 .js(及 JS/TS 源文件),在仓库中可观察到 lib/browser/api/ 下的 default-menu.tsipc-main-impl.ts 等文件均遵循此命名。注意代码中大量使用 ES6+ 语法:constlet 取代 var、箭头函数取代 function () { }、模板字符串取代 + 拼接。

.oxlintrc.json 中,这些约定被固化为可执行规则,典型示例:

  • no-varprefer-const(destructuring 全开)为 error
  • eqeqeqalways,但对 null 比较放行)、curlymulti-line)、no-unused-varsyoda(禁止 never)等均强制开启;
  • 开启了 import/no-duplicatesimport/no-absolute-pathimport/first 等导入规范类规则;
  • 默认将 correctness 大类整体关闭,避免 oxlint 的实验性正确性检查与既定代码产生冲突;
  • 针对 spec/ 下的测试代码,加载了两个私有 lint 插件(见 .oxlintrc.json):no-only-tests(禁止 .only 残留导致 CI 只跑部分用例)与 no-floating-spec-helpers

文件命名与常量风格值得单独强调:

// 符合规范:常量若是原始值,用全大写 + 下划线
const NUMBER_OF_RETRIES = 5;
const DEFAULT_TITLE = 'Electron';

格式化由 oxfmt 统一接管

除 lint 外,仓库用 oxfmt 做 JS/TS/MJS/CJS 的格式化:

# 只检查不修改
npm run lint:fmt

# 直接写入修正
npm run format

format 脚本(见 package.json)覆盖 {lib,spec,script,build,default_app,npm,typings} 等目录下的 *.{js,ts,mjs,cjs} 文件。同时 lint-staged(package.json)会在 pre-commit 时对暂存的 .js/.ts 自动执行 script/lint.js --js --fix --onlyoxfmt --write,从源头消灭格式分叉。

命名规范:与 Node.js 一致的大小写策略

Electron API 的命名沿用 Node.js 的大小写体系,共四档(coding-style.md 原文):

  • 模块本身是类:用 PascalCase。典型代表是窗口类 BrowserWindow,对应文档 docs/api/browser-window.md
  • 模块是一组 API:用 camelCase。如全局快捷键模块 globalShortcut,对应 docs/api/global-shortcut.md;同理还有 appsessiondialog 等单复数混合风格模块;
  • API 是某对象的属性且足够复杂、可独立成章:用 mixedCase,如 win.webContents(它是 BrowserWindow 实例上承载页面控制能力的复合对象,可参考 docs/api/web-contents.md 中对 webContents 的完整章节化描述);
  • 其他非模块 API:使用自然语言标题,如 <webview> 标签(见 docs/api/webview-tag.md)或 Process 对象(见 docs/api/process.md)。

这套大小写约定同时外溢到文档文件名docs/api/ 下每个文档都以小写 kebab-case 命名(browser-window.mdglobal-shortcut.md),与模块自身的导出名错开,读者可凭大小写快速区分"文件"与"标识符"。

方法 API 优先 getter/setter 而非 jQuery 风格

设计新 API 时,Electron 倾向使用成对的 getter/setter 代替 jQuery 式"一函数多用"写法。例如:

  • 推荐 .getText().setText(text)
  • 不推荐 .text([text])(参数可选时读、传入时写)。

这一偏好背后有现实工程原因:单函数带可选参数会让类型签名含糊,调用方无法静态确定是读还是写,且对 undefined 这类合法值的处理存在歧义。仓库在规则层面也配套了约束:.oxlintrc.jsonaccessor-pairs 设为 errorsetWithoutGet: trueenforceForClassMembers: true,确保类中写了 setter 必须配套 getter(见 .oxlintrc.json),从机制上鼓励成对设计。

收尾自查:提交前跑一遍全量流水线

风格文档的意义最终要落到可重复执行的检查上。综合 coding-style.mdstyle-guide.mdscript/lint.js 的解析结果,可整理出一套提交前的自检流程:

# 1. 全量检查(等价于依次执行 cpp/objc/js/python/gn/patches/markdown 七个 linter,
#    以及 lint:fmt + lint:docs + lint:chromium-roller,见 package.json)
npm run lint

# 2. 只想检查自己改动过的文件(基于 git 暂存区)
node ./script/lint.js -c

# 3. 只想检查指定文件,并允许自动修复
node ./script/lint.js --js --fix --only -- lib/browser/foo.js

script/lint.js 支持的语言开关(默认全开)为:--cpp--objc--javascript(别名 --js)、--python--gn--patches--markdown;其中 --gn 分支以 gn format --dry-run 校验 GN 构建文件的排版,--patches 分支校验 patches/ 下每个 .patch 文件是否在 patches/config.json 描述的目录与 .patches 清单中登记,且每条补丁必须附带"为什么存在、如何移除"的说明。

最后提醒三点容易忽略的细节:所有源文件与 Markdown 必须以换行符结尾;.js/.ts 文件名必须用连字符命名;新增 API 一律参考 docs/development/style-guide.md 中规定的模板格式写文档,并保证文档代码块的 Node 内置模块使用 node: 前缀。规范解决的是"仓库越大越难保持一致"的问题——当每个文件都长得像同一个人写的,代码审查的重点就能从风格争论回到真正的逻辑正确性上。

登录后查看全文
热门项目推荐
相关项目推荐