
VS Code 经常被一句话概括成“Electron + TypeScript 写的代码编辑器”。这句话不能算错,但它几乎跳过了 VS Code 最值得研究的部分。
真正让 VS Code 成为今天这个形态的,不是 Electron 本身,而是一套围绕分层、运行环境隔离、服务抽象、扩展宿主和远程执行建立起来的架构。它让同一套核心能力可以同时运行在 Windows、macOS、Linux、浏览器,以及 SSH、WSL、容器、Codespaces、Remote Tunnel 等远程环境中,同时尽量保持一致的交互与功能模型。
本文基于 Microsoft 当前公开的 microsoft/vscode 主仓库与官方架构文档,从工程实现角度拆解 VS Code 为什么能做到这一点。
一、先分清:GitHub 上的 vscode,不完全等于你下载安装的 VS Code
Microsoft 在 GitHub 上公开的仓库叫 Code - OSS。它是 VS Code 产品的主要开源代码基础,采用 MIT License;我们日常从官网下载安装的 Visual Studio Code,则是在 Code - OSS 基础上加入 Microsoft 的产品级配置、品牌和部分服务后形成的发行版。
这个区别很重要,因为研究源码时,我们研究的是 VS Code 的核心工程体系,而不是某个打包后的二进制产品。
二、VS Code 的真正核心:不是 Electron,而是“分层 × 运行环境”
VS Code 的核心代码主要位于 src/vs。当前仓库把它明确划分为几个有顺序约束的层:
- base:最基础的工具、事件、生命周期、UI 基础设施等。
- platform:跨模块共享的平台服务,以及依赖注入基础设施。
- editor:Monaco Editor Core,也就是编辑器核心。
- workbench:真正的 VS Code 工作台,包括编辑区、侧栏、面板、Notebook、菜单、状态栏等。
- code:桌面应用入口,包括 Electron Main、Shared Process、CLI 等。
- server:远程开发所需的服务端入口。
- sessions:当前主分支中进一步独立出的 Agent Sessions 窗口层。
这些层不是随意分目录,而是有严格的单向依赖关系:高层可以依赖低层,低层不能反向依赖高层。这样可以防止一个大型桌面应用随着功能增长逐渐变成互相调用、无法拆分的“巨型泥球”。
第二个维度:同一层内部再按运行环境拆分
VS Code 还有一个更关键的设计:同一层代码继续按照运行环境区分,例如:
common:只使用基础 JavaScript 能力,可在多种环境复用。browser:允许使用 DOM、Web API。node:允许使用 Node.js API。electron-browser:运行在桌面渲染侧,只开放有限的 Electron 通信能力。electron-utility:运行在 Electron Utility Process。electron-main:运行在 Electron 主进程。
可以把它理解成一个二维矩阵:纵向是产品层次,横向是运行时环境。
common browser node electron-*
base ✓ ✓ ✓ ✓
platform ✓ ✓ ✓ ✓
editor ✓ ✓
workbench ✓ ✓ ✓
code ✓
server ✓ ✓
这个设计比“到处写 if (isWeb) / if (isWindows)”成熟得多。平台差异被限制在边界实现里,大量核心逻辑天然就是跨平台的。
三、技术栈:VS Code 到底用了什么?
从技术栈上看,VS Code 是一个典型的“Web 技术做核心 UI,但不是普通 Web App”的大型客户端系统。
| 领域 | 主要技术 | 作用 |
|---|---|---|
| 核心语言 | TypeScript | 核心源码、Workbench、平台服务、扩展 API 等 |
| 桌面容器 | Electron | 跨 Windows/macOS/Linux 的桌面运行时与系统集成 |
| 渲染层 | Chromium / Web API / DOM | 统一桌面 UI 与浏览器端 UI 的技术基础 |
| 编辑器 | Monaco Editor Core | 文本模型、编辑、光标、选择、代码编辑体验等 |
| 扩展运行时 | Node.js / Web Worker | 分别承载桌面、远程和 Web 扩展 |
| 终端 | xterm.js + node-pty | 终端渲染和本地伪终端进程 |
| 搜索 | ripgrep 等 | 高性能文件与文本搜索 |
| 语法高亮 | TextMate / Oniguruma,另有 Tree-sitter WASM 能力 | 语言语法解析与高亮基础 |
| 测试 | Mocha、Playwright 等 | Node、浏览器与端到端测试 |
截至本文撰写时,仓库主分支的构建配置目标已经指向 Electron 42.x。具体版本会持续升级,但架构思路并不依赖某一代 Electron。
四、Monaco 只是编辑器,不是 VS Code
这是理解 VS Code 时最常见的误区之一。
Monaco Editor 是 VS Code 的编辑器核心,不是整个 VS Code。 它主要负责文本编辑器本身;文件树、Git、调试、终端、设置、搜索、Notebook、扩展管理、窗口布局、命令系统等,都属于更上层的 Workbench。
所以一个“基于 Monaco 的网页编辑器”和一个“VS Code 级开发环境”,工程复杂度完全不是一个量级。
五、Workbench:VS Code 为什么不像普通网页应用?
VS Code 的主界面虽然运行在 Web 技术栈上,但它并不是一个以 React/Vue 组件树为核心组织方式的常规 SPA。它自己的核心抽象更接近:
- Service:文件、配置、命令、存储、窗口、生命周期等能力以服务形式提供。
- Dependency Injection:通过构造函数注入服务,减少模块之间直接耦合。
- Contribution:Explorer、Search、Debug、Terminal 等功能以“贡献”的方式挂接到 Workbench。
- Command / Context Key / Menu:命令、快捷键、菜单和状态条件形成统一的交互模型。
因此 VS Code 的 UI 更像一个可插拔工作台内核,而不是一个不断向页面组件树里添加功能的网页。
六、桌面版为什么要多进程?
如果把所有事情都塞进 Electron Renderer,任何一个插件、终端、搜索任务或文件监听卡住,都可能直接拖死整个窗口。VS Code 没有这么做。
可以把桌面版粗略理解成下面这样的结构:
Electron Main
├─ Window / Renderer / Workbench
├─ Shared / Utility Process
├─ Extension Host
├─ PTY Host
├─ Search / File Watcher 等后台能力
└─ 其他按需进程
其中最关键的是 Extension Host。第三方扩展默认不会直接跑在主 UI 线程里,而是在独立的扩展宿主中执行。这样即使某个扩展出现高 CPU、阻塞或异常,VS Code 仍然有机会监测、诊断和隔离问题。
这并不是完全沙箱化——同一个 Extension Host 中的多个扩展仍可能互相影响——但它已经比“插件代码直接注入主界面进程”安全和可维护得多。
七、桌面、Web 为什么能保持高度一致?
VS Code 官方源码组织里有三个非常能说明问题的入口:
workbench.common.main.ts:桌面和 Web 共同能力。workbench.desktop.main.ts:只在桌面端加载的能力。workbench.web.main.ts:只在浏览器端加载的能力。
这意味着它不是分别维护“桌面版 VS Code”和“网页版 VS Code”,而是先最大化共享 Workbench,再在边缘替换平台实现。
例如同样是“文件系统”这个概念,桌面端可以落到本地文件系统,Web 端可以连接虚拟工作区、远程文件系统或浏览器能力;上层功能面对的却仍然是统一的服务接口。
所以 VS Code 多端一致性的核心不是“Electron 让三个桌面系统长得一样”,而是:
把平台差异压缩到基础设施边界,把产品行为尽量留在共享层。
八、Web 版不是桌面版“阉割后截图搬过去”
vscode.dev、github.dev 的 Workbench 运行在浏览器中,但浏览器没有完整 Node.js、不能任意启动本地进程,也受沙箱限制。
VS Code 的处理方式不是伪装这些能力存在,而是引入 Web Extension Host。Web 扩展在 Browser Web Worker 中运行,通过 browser 入口加载,只能使用浏览器允许的能力和 VS Code 提供的抽象 API。
因此一个扩展如果只依赖 VS Code API,而不是直接依赖 fs、本地 shell 或原生 Node 模块,就更容易同时支持桌面和 Web。
这也是一个很成熟的跨平台原则:不是让所有平台拥有同样的底层能力,而是让它们尽量遵守同一个上层契约。
九、Remote Development 的关键:把 UI 和计算位置解耦
VS Code Remote 更能体现这套架构的价值。
使用 SSH、WSL、Dev Container、Codespaces 或 Tunnel 时,界面仍然可以运行在本地桌面或浏览器,但工作区相关能力可以运行在远端的 VS Code Server 和 Remote Extension Host 中。
本地 / 浏览器
└─ Workbench UI
└─ RPC / Remote Protocol
└─ 远端 VS Code Server
├─ Remote Extension Host
├─ 文件系统
├─ Debug / Language Tooling
└─ Terminal / Workspace Process
这样做有两个直接收益。
第一,UI 不需要和代码、编译器、容器处在同一台机器;第二,语言服务、调试器、命令行工具可以直接运行在真正的工作区旁边,避免把大量文件和计算结果来回搬运。
官方甚至把扩展进一步区分为偏 UI 的扩展和偏 Workspace 的扩展,由 VS Code 自动决定它应该运行在本地、Web Extension Host,还是远程 Extension Host。
十、多平台一致性,不等于抹掉平台差异
VS Code 在 Windows、macOS 和 Linux 上的主工作区高度一致,但系统菜单、窗口行为、标题栏、快捷键、文件对话框、凭据存储、进程管理、系统主题等仍然需要平台专属实现。
成熟的跨平台架构不是追求“所有平台一行差异代码都没有”,而是明确:
- 哪些体验应该统一;
- 哪些能力必须遵守操作系统习惯;
- 平台差异应该被限制在哪一层;
- 上层业务是否能够不感知底层差异。
VS Code 的优势恰恰在于:它没有为了追求所谓“原生”而维护三套完整 UI,也没有为了追求“跨平台”而拒绝调用系统原生能力。
十一、Electron 为什么在 VS Code 身上没有明显的“网页套壳感”?
这也是 VS Code 很值得研究的地方。
Electron 只决定了应用拥有 Chromium + Node/Electron 能力,它并不决定产品最终是否像网页。VS Code 之所以不像普通后台管理系统,是因为它从产品和工程两端都没有把自己当成普通网站:
- 拥有完整桌面窗口生命周期和多进程模型;
- 大量键盘优先、命令优先的交互;
- 统一的命令、菜单、Context Key 和快捷键体系;
- 高密度但稳定的工作台布局;
- 终端、调试器、文件系统等深度系统能力;
- 严格控制的设计语言和扩展贡献位置;
- 平台特有能力通过 Electron Main 或专用服务接入,而不是在页面层硬编码。
所以“Electron 应用像不像网页”,本质上更多是产品架构、交互模型与系统集成深度的问题,而不是 Electron 这个框架本身的问题。
十二、VS Code 架构最值得借鉴的 6 个原则
- 共享核心,而不是复制多个客户端。 桌面和 Web 先共用 Workbench,再替换平台边界。
- 平台差异必须有明确归属。 common、browser、node、electron-* 的划分能阻止平台代码污染核心逻辑。
- 大型客户端必须多进程和故障隔离。 插件、终端、后台任务不应和主 UI 绑死。
- 扩展系统首先是架构问题,不是“允许别人插 JS”。 API、Extension Host、Contribution Points、运行位置决策共同组成了真正的扩展平台。
- 依赖抽象契约,而不是依赖具体环境。 同一个 Service 在桌面、Web、Remote 可以有不同实现。
- 一致体验来自稳定的产品语义。 Explorer、Editor、Panel、Command、Menu、Status Bar 等区域和交互规则长期稳定,用户才会觉得这是“同一个 VS Code”。
结语
如果只看技术名称,VS Code 似乎就是 TypeScript、Electron、Chromium、Node.js 和 Monaco 的组合;但真正让它成为顶级跨平台开发工具的,是这些技术背后的组织方式。
它把编辑器内核、工作台、桌面壳、浏览器运行时、扩展系统、远程服务端拆成可以组合的层,又通过统一服务契约、入口文件和运行环境边界,把不同平台重新拼成一种高度一致的产品体验。
这也是 VS Code 最值得学习的地方:跨平台不是“写一次,到处运行”这么简单,而是先设计一套能容纳差异、限制差异、复用核心的系统。


评论