为什么要研究vs??codevscode是一款优秀的开源编辑器。从代码复杂度和架构来看,无疑是Web前端领域最值得学习和研究的目标之一。通过开源代码,我们可以看到如何组织超过一百万行的代码来保证开发效率和可扩展性。我简单介绍一下这个团队的背景。vscode的总经理兼设计师ErichGamma是《设计模式:可服用面向对象软件基础》的四人帮之一。这本书也为我们目前所见的各种设计模式奠定了理论基础。此外,ErichGamma还参与了Eclipse的设计,同时开发了Java界最著名的单元测试框架Junit。除负责人外,其他核心成员均曾供职于IBM,拥有丰富的编辑经验,平均年龄40多岁。新成员多为开源社区知名插件专家,例如Gitlengs和Vetur的作者。我们有理由相信,我们可以从VSCode源码中获得丰厚的宝藏。多进程架构,跨平台兼容mac/linux/window,vscode采用electorn+web技术。从性能上看,传统的复杂跨平台客户端要么基于C++自研UI框架,要么基于C++Qt框架。为什么vscode选择web技术来实现整个编辑器,我只能从以下几个角度来推测:Erich在微软的首要任务是设计一个基于浏览器的在线Monco编辑器,这样可以很方便地集成到AzureDevOps和其他内部产品中微软的,Monco本身设计的很好,可以在它的基础上扩展成更复杂的编辑器,可以延续原有的体验。另一方面,云编辑器是一种趋势。使用web架构可以更好的适应未来的云化(远程vscode真香)。多进程+多线程vscode客户端的主进程是基于electorn的宿主环境,search模块是从主进程创建的node子进程。整个编辑器view运行在electron提供的render进程中,渲染层相当于浏览器的webview。众所周知,vscode最强大也是最早的就是它基于web的插件生态。其实通过插件来扩展编辑器是很常见的。曾经火爆的开源ideeclipse也以其强大的插件系统着称,但是eclipse最严重的问题之一就是大量的第三方插件让ide本身变得臃肿。同样,github官方也开发了基于electron的atom编辑器,在插件架构下具有强大的定制能力。插件开发者甚至可以随意修改页面弹出任何内容,但插件滞后的问题一直被大量用户诟病。我们看到Plugin是一只强大的母老虎,在学习如何使用它的同时弄清楚如何将它关在笼子里。或许是出于以往行业案例的各种教训,vscode采取了中庸之道。首先,它将所有插件隔离在独立的进程中,这是为了保证主进程和渲染进程不被第三方扩展阻塞。另一方面,它充分抽象了插件进程的行为,提供了一个主要的、可控的API。atom的设计失败之一就是给了扩展太多的权力,而vscode团队基于丰富的编辑器开发经验做了一个取舍,以语言处理和数据处理为首要能力,一无所有对界面UI的扩展限制非常严格,因此无法调整vscode的侧边扩展UI。vscode的插件都绑定在worker中,插件逻辑以多线程的方式执行。这种架构的好处是减少了第三方插件导致的编辑器滞后。开发调试快速开发大部分代码是开源的,只需要上传一个内网仓库的临时备份到github。但是vscode开源的不仅仅是代码。备受业界好评的是整个团队通过github直接管理vscode开发的全过程,从需求收集到issue跟进以及项目进度管理都在一个开源开放、人人都可以参与的平台。介绍这些是为了说明,如果你对大型开源项目的协作感兴趣,可以去github看看微软是怎么管理的。首先clone整个仓库:gitclonehttps://github.com/microsoft/vscode.git然后安装依赖yarn。需要注意的是,通过npmi安装会被vscode禁用。我们必须先安装yarn,然后再使用yarn安装依赖。npmyarn-gyarn启动web版vscode:yarnweb后会自动构建vscode源码,然后自动开启开发端口,并打开默认浏览器。如果正常,你会看到浏览器打开如下页面:一个需要注意的陷阱是:由于打开了web调试页面,vscode会先请求一个远程地址加载内容,返回的内容会被当做作为模拟的本地文件。如果你的网络不通,或者你没有科学的上网姿势,那它永远是一片空白。解决方法是启动时在终端挂代理:exporthttp_proxy=http://127.0.0.1:xxxx对于经常需要参与github开源项目的同学,建议先使用代理,否则适配类似的开源项目。出问题会踩坑。另一个临时解决方案是只修改当前版本的vscode的源代码。下面的文件是vscode获取适配模拟文件的逻辑。node_modules/gulp-remote-retry-src/index.js,其中content是从远端加载的数据,可以修改下面代码中的content变量为自己的内容。注释掉远程加载的逻辑,不用访问外网也可以调试。varfile=newFile({cwd:"/",base:options.base,path:url,contents:newBuffer(JSON.stringify(body)),});cb(空,文件);启动桌面vscode./scripts/code.sh我们可以使用vscode调试vscode(娃娃娃娃禁用),点击vscode的调试按钮,会看到多个进程的断点调用栈。在调试和开发vscode之前,我们需要了解上面提到的多线程架构,了解vscode的整体文件结构和各个模块的功能。以下是我的一些梳理。文件结构:基本结构:vscode整体代码分为public、dependencyinjectionservice、独立编辑器内核、workbench,不熟悉这个概念的同学可以提前了解一下相关知识,有助于加深对vscode架构的理解。base:UI模块,提供通用的对象实体和最基本的用户界面platform:定义了对vscode的服务注入支持(依赖注入封装)和基础服务(service)editor:一个独立可用的编辑器模块,可以直接打包成一个独立的Monaco编辑器外部使用workbench:Workbench可以理解为为基本的Monaco编辑器提供了一套完整的shell框架,比如文件管理器、状态栏、菜单栏。使用Electron向VSCode提供桌面软件代码:这里是整个VSCode的入口文件,包括各个平台的html和主文件平台/环境级跨平台是vscode设计的特点,我们需要兼容同时有浏览器、node、桌面,以及沙箱环境,如果一个模块涉及多个环境,需要通过文件夹命名约定来区分环境并进行约束。下面是vscode的环境区分和调用规则:common:这里的源码只能是基本的javascriptAPI,可以在任何环境下运行。任何环境浏览器都可以依赖:该目录下的代码可以访问浏览器API,比如DOM可以依赖环境:commonnode:调用nodejs的API逻辑可以依赖环境:commonelectron-sandbox:代码可以运行在electron沙箱中,遵循Sandbox规则,可以访问DOM和一些主要API(根据进程通信规范)依赖环境:common,browserelectron-browser:可以调用electronrenderer-process渲染进程api。依赖模块:common、browser、nodeelectron-main:可以调用Electron主进程api环境可靠:common和node上的规则没有强制验证方式,但约定大于配置,时间地点模块编写时共识可以在vscode注释中体现。关于monaco,我们通常所说的monaco和vscode其实就是汽车和引擎的关系。只是这个引擎设计得足够好,不仅适用于vscode,也适用于其他品牌的汽车。vs/edit目录下的代码可以独立打包生成一个名为monaco-editor-core的npm包,这个包最终会作为monaco的核心依赖。Monaco可以看作是一个简化版的编辑器,它提供了最基本的编辑功能,暴露了各种API,并通过特定的外部服务来决定编辑器的行为。目前市面上很多代码编辑器工具都是基于monaco的。因此,编辑路径下的代码不能添加任何vscode特有的功能,只依赖于公共服务。vs/edit/browser:编辑器可以访问浏览器部分的逻辑,基本抽象了编辑器UI组件的行为vs/edit/common:编辑器的核心抽象,符合common规范,可以运行onany环境,它有编辑器的全天行为模型,以及视图模型抽象。以上两个文件基本上就是Monaco编辑器的核心Model和View。vs/edit/contrib:对于编辑器的基本功能,如搜索、悬停、复制粘贴、格式化、消息等独立的实体包,vscode放在该目录下。它基本上依赖于vs/edit/browser和vs/edit/common的逻辑。这些基本贡献(扩展)是Monaco的一部分,将内置到整个vscode或独立版本中。vs/editor/standalone这里只是用来将核心编辑器独立打包成一个Monaco的shell。它不会被任何模块依赖。在Workbench上,我们看到vs/edit/是编辑器的核心,可以独立发布。但是vscode给我们提供的一定是一个功能齐全、丰富的代码编辑器,应该包括文本搜索、git管理、调试、导航、菜单等功能。Workbench目录下的模块承担了这个角色,workbench——为核心编辑器提供了一套完整的组合功能。另外,它对monaco的引用是源码级的模块依赖,而不是npm包,在这里可以感受到monaco抽象的精妙之处。vs//workbench/apivscode各个运行环境的内部接口,包括对主进程和插件进程接口的封装vs//workbench/browservscode浏览器环境的入口逻辑和基本视图框架定义vs//workbench/contribvscode的独立业务功能封装,vscode将其抽象为独立的扩展点(contrib),挂载在vscode的基本视图上,如备份、日志、搜索、调试等,每个contrib之前都是独立的,不应该互相依赖。他们共享底层的api基本视图框架。contrib就像是vscode团队利用自己对IDE代码的理解,对Monaco进行了进一步的强化和封装。vs/workbench/electron-browserelectron桌面环境入口和基本视图定义vs/workbench/electron-sandboxelectron沙箱环境入口和基本视图定义vs/workbench/servicesvscode内部逻辑是纯业务功能的抽象.contrib的区别是纯粹的、与视图无关的服务。感觉以上就是对vscode整体目录结构的简单分析。我感觉不同的是vscode的整体设计其实非常灵活。通常,大型的前端项目,从可维护性的纵向角度,会划分出相对清晰的层级,并尽量以某种形式对核心模块进行隔离,以减少服务变更对稳定性的破坏。VScode更喜欢水平隔离模块。这种灵活的架构可以很方便的演进和扩展,但是对开发者的要求比较高,必须熟悉底层机制。冰山一角暂时只是对vscode的简单初步探索。可以肯定地说,我们所了解的只是冰山一角,只是远观其形。其内部复杂的细节尚未分析。例如,如何管理成千上万的服务和模块,如何管理内存,Edit(Monaco)排版和布局设计,如何支持任何编程语言(LanguageServerProtocol)的语法高亮和提示,代码调试服务如何工作(调试适配器协议)?如何设计主进程和插件进程(ICP)之间的通信?如何优化大文本的性能等等。期待下次进一步了解。——————文档信息标题:初探vscode(一)发布时间:2022-01-09笔名:混沌符王
