最近在 27rabbit 的影响下,突然对 VSCode 本身以及各种插件的实现产生了兴趣;也刚好本来就没有系统学习过 VSCode 的使用,平常使用的配置也都是一知半解,所以尝试学习一下。
如我之前的各种文章一样,我希望让读者能够通过我的文章对 VSCode 有一些基础的了解。但也是因为我没有打算深入系统地学习,只是从实用出发,在概念层面上理解其实现方式,因此内容会相对比较浅显。
我啥都不懂,如果看到有什么笨蛋问题,请不要骂我 QAQ
开始之前,先自己想想
在开始学习之前,我先来自己尝试回顾一下我用过 VSCode 的什么功能,以及我猜测它的解决方案:
- 作为一个纯粹的编辑器:
- 只讨论文件目录和文件内容编辑的功能。这个语境下的解决方案似乎比较浅显,我们只需要一个目录树,一个文本编辑器;一个中间层适配不同的操作系统,与文件系统交互;一套前端框架用于展示这一切。
- 我猜测文件可以通过加载到内存的方式来展示,因为我遇到过尝试打开一个 > 100MiB 的文件被 VSCode 劝阻的情况。也许它会事实将文件内容或文件更改 dump 到磁盘的某个地方,因此可以在 VSCode 重启时不丢失编辑内容。
- 确实好奇过文本编辑器是如何高效处理大文件的,这个可以探索一下。
- 内置 terminal:
- 这个功能算是最常用的了。内置 terminal 里可以使用
code
这样外面没有的命令,以及可以自动进入 venv 之类的。听起来可以通过启动的时候跑一个自己的脚本实现,比较简单。 - 如何在前端渲染 terminal,那其实跟自己写个 terminal 差不多?实在不懂前端,可以看一下,但是这个解决方案估计不是 VSCode 的一部分。
- 这个功能算是最常用的了。内置 terminal 里可以使用
- 插件系统:
- 插件的功能非常丰富,高度定制化。如果我来搞的话,大概会定义一系列事件,例如按下某个案件,或者文本有编辑;插件可以监听事件。有一些插件也许需要一些运行时的信息甚至一个单独的进程,那可能是在 VSCode 启动的时候,或者某些事件发生时,初始化这个进程跑在那。这样想的话,安装插件,大概就是下载对应的代码,以及注册相关的回调。
- 指令系统,这个好像就是用户主动调用来触发一些行为或者事件。好像没啥特别的。
- 显示:
- VSCode 有各种乱七八糟的配置,当然包括文本的显示设置。因此前后端的交互应该是高度可配置的。这样听起来,与其提供一系列的显示设置,不如让后端直接给前端提供标记好的 HTML?
- 这个好像不解决根本问题,插件作者应该不乐意直接写 HTML?那听起来就得像 word 一样对各种不同的区域进行抽象,然后给插件作者充分可配置的能力?
- 具体的一些插件:
- 还挺好奇一些插件的实现的,之前好像看到后面会有 server 在跑。可以看完主线之后了解一下。
所以听起来,我会搞这样几个模块来做实现:
- 一个前端。它处理一些「大」层面上的内容,例如窗口有哪些部分及调整,以及响应用户操作。具体的窗口部分显示啥东西,后端提供完整 HTML;用户操作后的行为,直接交给后端。
- 一套高度可定制化的显示内容的抽象。举个例子,目录树在不同插件的影响下,每个 entry 可能会有不同的图标、标记、颜色等。这套抽象可能可以用于指定一些具体的 style,也可能直接支持写 html。这套抽象负责这些内容最终转换为给前端的 html。
- 一套事件系统,所有变化都由这里分发的事件触发。各类基本功能,以及插件和指令,都由这里分发。分发后,handler 会影响显示内容。
以下是学习过程中的各种随机记录,不属于正文部分。
学习记录
和 Gemini / ChatGPT / DeepSeek 聊天
- 为我简述 VSCode 的实现架构
- Electron + 核心编辑器 + 扩展模型 + Language Server Protocol + 调试架构 + 工作区 + 用户界面
- 为我简述 Chromium, Node.js 和 Electron
- 定义、关键特性、用途,看不太懂
- 为我详细介绍一下 Node.js
- 简介、特性、场景、用途,也看不大懂
- 「Node.js 是一个开源、跨平台的 JavaScript 运行时环境,它允许开发者在服务器端运行 JavaScript 代码。」为什么不能直接运行 JavaScript?
- 因为 JavaScript 为浏览器设计,主要运行环境是浏览器。一方面很难和操作系统交互,另一方面缺乏服务器端长时间运行的功能。
- 所以说,浏览器是 JavaScript 的运行环境,Node.js 也是,后者更适合服务器端,也提供更多完善的 API。
- 那为什么非要用 JavaScript
- 历史背景:Node.js 创始人想搞高性能的 Web 服务器,认为 JavaScript 的事件驱动模型很好。JavaScript 的事件驱动、非阻塞 I/O 模型非常适合处理高并发的网络请求,这是服务器端开发的关键需求。
- 为什么现在大量新的 Web 服务仍然使用传统的实现方式?它们相较于 Node.js 又有什么优势?
- 所以说,Node.js 选择 JavaScript 是因为其事件驱动、非阻塞 I/O 的特性,用以高效处理大量并发;而旧的基于 C++/Java/Python 的架构,在生态系统、稳定性、性能等方面存在优势。那为什么当时的技术选择,不是基于旧架构实现一套事件驱动、非阻塞 I/O 的框架呢?
- (包含其他问题:「为我使用 js 和 python 分别展示一下」「展开讲讲 Node.js 的事件驱动机制」)
- 因为 JavaScript 原生就是事件驱动的,在其他语言中实现的门槛和学习成本都较高。JavaScript 原生是浏览器中的单线程事件循环模型。而且服务端也能用 JavaScript 的话,前后端使用同一套语言,对开发者来说更友好。
- Node.js 使用了 libuv 库。Node.js 的主线程执行一些同步的代码,过程中可能注册一些异步的任务或者事件监听,这些任务和事件会被 libuv 的线程池处理,或者基于 epoll 等系统工具等待。在任务结束或事件触发时,对应的回调函数会被加入到任务队列中;主线程在空闲时会查看队列,从中取出任务执行。
- 举个例子:下面的代码是同步的,主线程首先会完成这里所有代码的执行。但是,其中
fs.readFile
是异步的,主线程只是创建这个任务,libuv 会处理这个任务。同时,主线程还定义了回调函数,也就是在任务结束时console.log
打印文件内容。const fs = require('fs'); fs.readFile('hello.txt', 'utf-8', (err, data) => { console.log('文件内容是:', data); });
- 为我更详细介绍 Chromium
- 多进程模型 + Blink 渲染引擎 (渲染三大件) + V8 引擎 (跑 js) + 网络栈 + UI 层框架 + 安全机制
- 一个基于 Chromium 的浏览器或者 App,通常在 Chromium 以外还要做什么?我只是想基于我熟悉的 App 或者浏览器,来了解 Chromium 的功能边界
- UI、生命周期管理、插件系统、业务逻辑、操作系统能力接入需要自己搞,别的交给 Chromium
- 为我介绍 Electron
- 它用于将 Web 技术封装为原生桌面应用。它结合 Chromium 和 Node.js,这样可以在一个桌面应用中同时使用前端 Web 技术和 Node.js 的后端能力。