Eclipse Theia 揭秘之技术架构篇
Cloud IDE
随着前端开发的发展更迭,前端日常开发工作变得愈发复杂愈发深入,同时前端工程中从项目初始化、编译、构建到发布、运维也变得细化而成熟。本地开发环境存在开发机性能要求高、开发环境配置复杂、依赖特定设备、复杂工程管理难等问题,Cloud IDE 很好的解决了这些问题。最近几年,Cloud IDE 在开发全流程领域扮演者越来越重的角色,国内外很多厂商都在做 Cloud IDE。
- Coding:国内的云 IDE 产品
- Cloud9:亚马逊为其云计算服务提供的 IDE
- Eclipse Theia / Eclipse Che:Eclipse 老牌团队研发
- CodeSandbox:面向 Web 项目的云 IDE
- coder / code-server / vsonline:VS Code 云端版本
- Expo Snack:React Native 的云端开发环境
- RunKit:npm 官方配套
Cloud IDE 基础设施
Cloud IDE 架构
Cloud IDE 主要包含 Client、Server、Container Pool 三部分。
- Client: 客户端也是最重要的端,将代码编辑等本地功能移植到浏览器中。
- Server: 服务端也是控制端,包括管理数据交互及资源调度。
- Container Pool: 运行时,用户代码真正运行的容器环境。
三者之间最典型的架构如下图所示:
What is Theia?
Eclipse Theia 是一个可扩展的平台,基于现代 Web 技术(TypeScript, CSS 和 HTML)实现,用于开发成熟的、多语言的云计算和桌面类的理想产品。
- 官网:https://theia-ide.org
- 仓库:https://github.com/eclipse-theia/theia
Theia 为开发浏览器和桌面 IDE 提供了可扩展的平台,主要特性:
- Web AND desktop-based:提供 Cloud IDE 和 Desktop IDE 两种模式;
- Do not reinvent the wheel:不重复造轮子,Theia 复用其他框架、标准和技术,例如通过语言服务协议(LSP,Language Server Protocol) 和调试适配器协议 (DAP,Debug Adapter Protocols) 提供对多种语言的支持,支持 VS Code 拓展和 Git、终端等主流工具;
- For IDEs and domain-specific tools (not only code editors):Theia 的目标不是成为一个简单的代码编辑器,而是成为一个支持创建特定领域工具套件和集成开发环境的基础平台;提供基本的工作台框架,包括菜单、状态栏、视图概念、部件布局、工作空间抽象等,工作台可以通过自定义 UI 扩展进行扩展,比如菜单项、自定义视图和自定义编辑器;
- Extension first:模块化扩展机制,允许以模块化、可重用和可组合的方式实现功能,这些扩展可以针对前端及后端,该扩展机制还允许用户安装新特性;Theia 所有东西都是扩展,甚至包括项目本身提供的核心特性,像 Eclipse rich-client 平台一样,你可以在 Theia 中定制几乎所有的东西,甚至替换核心功能;
- Active community and vendor-neutrality:Theia 由 Eclipse 基金会托管,是一个与厂商无关的项目。
目前 Theia 社区已经有一些产品基于 Theia 构建:
- Gitpod
- Grahical Language Server Platform (GLSP) / EMF.cloud
Theia vs. VS Code
根据 Stack Overflow 2019 年 Developer Survey Results 中流行的开发者工具排行榜,VS Code 无疑是最流行的开发者工具。
VS Code 提供了开箱即用的产品,借助强大的插件生态实现下载各种增强功能的 VS Code Extensions,例如对各种语言的支持;而 Theia 是一个构建 IDE 和工具的框架,当然 Theia 也提供了多种启动方法,下文将详细介绍。
对比指标 | VS Code Product | VS Code Project | Eclipse Theia |
可用性概念 | ✔✔ | ✔✔ | ✔✔ |
技术栈和架构 | ✔✔ | ✔✔ | ✔✔ |
可扩展性和适应性 | ? | ✔ | ✔✔ |
可用的扩展和技术 | ✔✔ | ✔ | ? |
在线/离线功能 | ✔ | ✔ | ✔✔ |
开源 | ✖ | ✔✔ | ✔✔ |
生态系统和厂商无关性 | ? | ✔ | ✔✔ |
可用性概念:VS Code 主要关注代码,在用户界面中占用的功能非常少,它的可用性概念是围绕使用键盘,而不是鼠标。许多特性只能通过CLI或命令面板获得。Theia 和 VS Code 使用相同的代码编辑器(Monaco)、相同的窗口管理和非常相似的命令面板,甚至大多数默认的快捷方式都是相同的。
技术栈和架构:VS Code 和 Eclipse Theia 都基于 Web 技术,包括 TypeScript,HTML5,CSS 以及 Node.js。实际上 Theia 复用了 VS Code 大量的技术,例如结合 Monaco code editor 和 language server protocol (LSP) 的方式,通过 LSP 允许以标准化的方式将代码编辑器的 UI 特性与底层逻辑解耦。
可扩展性和适应性:VS Code 通过用户可安装的 VS Code Extensions 提供了一种扩展机制,提供一套内置的 API 用于扩展 VS Code。Eclipse Theia 提供了一个类似的扩展机制,但称之为 Theia Plugins。Theia 实现了相同的API,所以许多 VS Code 扩展也可以在 Theia中 使用(参见《如何在 Theia 中安装 VS Code 扩展》的文章)。简而言之,VS Code Extensions 和 Theia Plugins 在概念上几乎是一样的。通过内置的 API,可以很容易开发一个VS Code 拓展,拓展运行在特定的进程中可以很好的保证产品稳定性,以及允许在运行时安装。这种扩展模式促进了 VS Code 生态,但是缺点是只能基于公开的 API 进行拓展。虽然涵盖了许多用例,例如添加命令,视图或扩展代码编辑,由于无法预见,因此很难进行其他一些修改。Theia 官方说插件克服了 VS Code 拓展的一些限制,提供了更强大的扩展机制。Theia 作为一个平台,完全由 Theia 扩展组成,这些扩展通过依赖注入进行连接,这使得 Theia 非常灵活,你可以删除任何你不喜欢的东西,调整或扩展平台本身所触及的一切。后面我们再专门探究一下 Theia 拓展的原理。
可用的扩展和技术:Eclipse Theia 支持 VS Code 扩展 API,也可以在 Theia 中使用VS代码扩展,不过有两个限制条件:Theia 还没有覆盖 VS Code 定义的全部 API,存在部分不可用的情况,API 覆盖率可以参考 vscode-theia-comparator;VS Code 插件市场的使用条款限制插件,推荐从项目主页下载。
在线与离线功能:最开始 VS Code 和 Theia 的区别就是在线和离线的区别,Theia 的核心架构明确允许将产品部署为桌面应用程序,并通过浏览器在云中访问。目前 VS Code 和 Theia 都支持在线和离线功能。
开源:VS Code Project 是开源的,但是 VS Code 产品不是开源的,VS Code Project 基于 MIT 协议;Theia 是基于 Eclipse Public License (EPL) 协议。最大的限制是只允许 VS Code 产品连接 VS Code 拓展市场,其他产品不能直接使用 VS Code 拓展市场的服务。
生态系统和三方库独立性:Theia 复用了 VS Code 生态的大量代码,所以整体代码数量上相对少一些。整体上讲相比 Theia,VS Code 生态更强大,Theia 的优势在于由 Eclipse 基金会托管,众多 Eclipse 成员公司参与 Eclipse Theia 的贡献。
整体上来说,如果我们想开发一个侧重代码的工具让更多的人在现有的 IDE 中使用,VS Code 拓展或许是更好的选择;如果是需要提供比编辑器更多功能的定制 IDE,使用 Theia 是更好的选择。
How to launch Theia?
上面我们说了 VS Code 和 Theia 的区别,相信大家即使没有看过 VS Code 源代码,也通过 VS Code 产品包体验过 VS Code 的功能,那么怎么安装并启动 Theia 呢?Theia 提供了多种启动方式:
1.基于自定义 package.json 构建包启动
由于 Theia 及其扩展是 Node.js 包,而 Theia 应用程序是包的集合,因此启动 Theia 及所选扩展的一个非常简单的方法就是创建一个 package.json。
参考文档:Build your own IDE
2.基于预配置的 Docker Image 构建
- 镜像列表:https://github.com/theia-ide/theia-apps
- 官方默认镜像:https://hub.docker.com/r/theiaide/theia
3.从源代码克隆、构建和运行
参考文档:How to build Theia and the example applications
4.基于 Eclipse Che(托管运行时和工作区) 构建
如果你不想下载、部署或编译任何东西,而只想以托管的方式尝试 Eclipse Theia,那么您可以使用 Eclipse Che 来实现这一点。Eclipse Che 提供了一个工作空间服务器,即服务器,它可以承载一个或多个开发人员的开发环境。从版本 Eclipse Che 7 开始,Eclipse Theia 是 Eclipse Che 的默认 IDE (在 Che 中称为编辑器)。
参考文档:Eclipse Che vs. Eclipse Theia、How to install/run/try Eclipse Che
对于新手而言,推荐第一种方法方式去了解启动一个 Theia Application 工程,整体过程类似于一个普通的 Node 工程,本文不做赘述。
Theia Architecture
概述
Theia 被设计为一个可以在本地运行的桌面应用程序,也可以在浏览器和远程服务器之间工作。为了支持这两种工作方式,Theia 运行在两个独立的进程中,它们被称之为前端和后端,相互之间通过 WebSockets 上的 JSON-RPC 消息或 HTTP 上的 REST APIs 来通信。对于 Electron 而言,前端和后端都在本地运行,而在远程上下文中,后端运行在远程服务器上。前端和后端进行都有它们各自的依赖注入 (DI) 容器以方便开发者进行扩展。
前端
前端部分负责客户端的UI呈现。在浏览器中,它只是简单地在渲染循环中运行,而在 Electron 中,它运行在 Electron BrowserWindow 中,BrowserWindow 是包含 Electron 和 Node.js APIs 的浏览器窗口。因此,任何前端代码都可以把浏览器而不是 Node.js 作为一个运行平台。
启动前端进程将首先加载所有扩展包的 DI 模块,然后获取一个 FrontendApplication 的实例并在上面调用 start()。
const { Container } = require('inversify');// 应用前端部分配置
FrontendApplicationConfigProvider.set({"applicationName": "Theia"
});// IoC 容器
const container = new Container();
container.load(frontendApplicationModule);
container.load(messagingFrontendModule);
container.load(loggerFrontendModule);function load(raw) {return Promise.resolve(raw.default).then(module =>container.load(module))
}function start() {(window['theia'] = window['theia'] || {}).container = container;const themeService = ThemeService.get();themeService.loadUserTheme();const application = container.get(FrontendApplication);return application.start();
}module.exports = Promise.resolve().then(function () { return Promise.resolve(require('@theia/core/lib/browser/menu/browser-menu-module')).then(load) })....then(start).catch(reason => {console.error('Failed to start the frontend application.');if (reason) {console.error(reason);}});
后端
后端进程运行在Node.js上。我们使用express作为HTTP服务器,它可以不使用任何需要浏览器平台的代码(DOM API)。
启动后端应用程序将首先加载所有扩展包的DI模块,然后获取一个 BackendApplication 的实例并在上面调用 start(portNumber)。 默认情况下后端的 Express 服务器也为前端提供代码。
require('reflect-metadata');
const path = require('path');
const express = require('express');
const { Container } = require('inversify');
const { BackendApplication, CliManager } = require('@theia/core/lib/node');
const { backendApplicationModule } = require('@theia/core/lib/node/backend-application-module');
const { messagingBackendModule } = require('@theia/core/lib/node/messaging/messaging-backend-module');
const { loggerBackendModule } = require('@theia/core/lib/node/logger-backend-module');const container = new Container();
container.load(backendApplicationModule);
container.load(messagingBackendModule);
container.load(loggerBackendModule);function load(raw) {return Promise.resolve(raw.default).then(module =>container.load(module))
}function start(port, host, argv) {if (argv === undefined) {argv = process.argv;}const cliManager = container.get(CliManager);return cliManager.initializeCli(argv).then(function () {const application = container.get(BackendApplication);application.use(express.static(path.join(__dirname, '../../lib')));application.use(express.static(path.join(__dirname, '../../lib/index.html')));return application.start(port, host);});
}module.exports = (port, host, argv) => Promise.resolve().then(function () { return Promise.resolve(require('@theia/process/lib/node/process-backend-module')).then(load) })....then(() => start(port, host, argv)).catch(reason => {console.error('Failed to start the backend application.');if (reason) {console.error(reason);}throw reason;});
按平台进行区分
在扩展包的根目录下,包含如下子目录层级,按不同的平台进行区分:
- common:目录下包含的代码不依赖于任何运行时;
- browser:目录下包含的代码需要运行在现代浏览器平台上(DOM API);
- electron-browser:目录下包含了需要 DOM API 及 Electron 渲染进程特定的 APIs 的前端代码;
- node:目录包含了需要运行在 Node.js 下的后端代码;
- node-electron:目录包含了 Electron 特定的后端代码。
部署模式
Theia 支持三种部署模式。
1.Web Client, Remote Back-end (Cloud IDE)
前端是从远程服务器提供给本地浏览器的,并连接到远程后端。
2.Native Front-End, Local Back-end
基于Electron,IDE可以在本地运行前端和后端。
3.Native Front-End, Remote Back-end
基于Electron,只有前端会在本地运行,并连接到远程后端。
架构图
下图说明了主要组件及其连接方式:
依赖的组件:
模块名 | 版本 | 开源协议 |
electron | 1.6.2 | MIT |
express | 4.15.2 | MIT |
inversify | 3.1.0 | MIT |
monaco-editor-core | 0.8.2 | MIT |
monaco-editor | 0.8.3 | MIT |
monaco-languageclient | 0.0.1-alpha.2 | MIT |
ws | 2.2.0 | MIT |
reconnecting-websocket | 3.0.3 | MIT |
@phosphor/application | 0.1.5 | BSD-3-Clause |
@phosphor/algorithm | 0.1.1 | BSD-3-Clause |
@phosphor/domutils | 0.1.2 | BSD-3-Clause |
@phosphor/messaging | 0.1.2 | BSD-3-Clause |
@phosphor/signaling | 0.1.2 | BSD-3-Clause |
@phosphor/virtualdom | 0.1.1 | BSD-3-Clause |
@phosphor/widgets | 0.1.7 | BSD-3-Clause |
reflect-metadata | 0.1.10 | Apache-2.0 |
vscode-ws-jsonrpc | 0.0.1-alpha.1 | MIT |
vscode-languageserver | 3.2.0 | MIT |
扩展包
Theia 由扩展包构成,前端应用程序和主后端应用程序均包含多个扩展。 一个 npm 软件包可以公开一个或多个扩展,这些扩展可以被前端和主后端应用程序使用。一个扩展包就是一个 npm 包,在这个 npm 包中公开了用于创建 DI 容器的多个 DI 模块 (ContainerModule) 。通过在应用程序的 package.json 中添加 npm 包的依赖项来使用扩展包,扩展包能够在运行时安装和卸载,这将触发重新编译和重启。通过 DI 模块,扩展包能提供从类型到具体实现的绑定,即提供服务和功能。
Services 和 Contributions
依赖注入(DI)
DI 在 Theia 中是一个非常重要的部分,Theia 使用DI框架 Inversify.js 来连接不同的组件。DI 在创建时注入组件(作为构造函数的参数),从而将组件从依赖项中彻底解耦出来。DI 容器根据你在启动时通过所谓的容器模块提供的配置项来进行创建。
例如,Navigator 小部件需要访问 FileSystem 用来在树形结构中显示文件夹和文件,但是 FileSystem 接口的实现对 Navigator 来说并不重要,它可以大胆地假设与 FileSystem 接口一致的对象已经准备好并可以使用了。在 Theia 中,FileSystem 的实现仅仅是一个发送 JSON-RPC 消息到后端的代理,它需要一个特殊的配置和处理程序。Navigator不需要关心这些细节,因为它将获取一个被注入的 FileSystem 的实例。此外,这种结构的解耦和使用,允许扩展包在需要时能提供非常具体的功能实现,例如这里提到的 FileSystem,而不需要接触到 FileSystem 接口的任何实现。
Services
Service 只是一个提供给其它组件使用的绑定。一个扩展包可以公开 SelectionService,这样其它扩展包就可以获得一个注入的实例并使用它。
Contribution-Points
如果一个扩展包想要提供 hook 由其它扩展包来实现其中的功能,那么它应该定义一个 contribution-point。一个 contribution-point 就是一个可以被其它扩展包实现的接口。扩展包可以在需要时将它委托给其它部分。例如,OpenerService 定义了一个 contribution-point,允许其它扩展包注册 OpenHandler,你可以查看 这里 的代码。Theia 已经提供了大量的 Contribution Points 列表,查看已存在的 Contribution Points 的一个好方法是查找 bindContributionProvider 的引用。
Contribution Providers
一个 Contribution Provider 基本上是 Contributions 的容器,其中的 Contributions 是绑定类型的实例,要将类型绑定到 Contribution Provider,你可以这样做:
// messageing-module.ts
export const messagingModule = new ContainerModule(bind => {bind<BackendApplicationContribution>(BackendApplicationContribution).to(MessagingContribution);bindContributionProvider(bind, ConnectionHandler)
});
最后一行将一个 ContributionProvider 绑定到一个包含所有 ConnectionHandler 绑定实例的对象上。像这样使用:
// messageing-module.ts
constructor(@inject(ContributionProvider) @named(ConnectionHandler) protected readonly handlers: ContributionProvider<ConnectionHandler>) {
}
这里我们注入了一个 ContributionProvider,它的 name 值是 ConnectionHandler,这个值之前是由 bindContributionProvider 绑定的。这使得任何人都可以绑定 ConnectionHandler,现在,当 messageingModule启动时,所有的 ConnectionHandlers 都将被初始化。
Theia Services 的依赖注入机制借鉴于 VS Code,Contributions 机制借鉴于 Eclipse。从整体设计上看 Theia 的可拓展性更强大,后面我们再详细分析 Theia 的拓展机制原理。
参考
- 前端工程化下一站: IDE
- Cloud IDE
- 我在阿里做中后台开发
- Welcome (at Eclipse), Theia!
- The Eclipse Theia IDE vs. VS Code
- How to launch Eclipse Theia
- Multi-Language IDE Implemented in JS
- Theia Architecture
Eclipse Theia 揭秘之技术架构篇相关推荐
- Eclipse Theia 揭秘之启动流程篇
前言 在<Eclipse Theia 框架技术架构揭秘>一文中简单介绍了 Theia 框架整体的技术架构,接下来将通过系列文章从源码角度入手看一下 Theia 核心设计思路,本文从启动流程 ...
- Eclipse Theia 揭秘之拓展机制篇
前言 VS Code 之所以是最流行的开发者工具,与其强大的插件生态是分不开的,VS Code 生态内有各种增强功能的 VS Code Extensions,Theia 在 VS Code 拓展机制上 ...
- (一)人工智能大纲摘要:《人工智能发展白皮书-技术架构篇(2018年9月)》
以下博客的主要内容,摘自白皮书. http://www.caict.ac.cn/kxyj/qwfb/bps/index_1.htm < ...
- 专栏《乔新亮的CTO成长复盘》读书笔记(技术架构篇)
架构决策能力不但非常关键,而且是技术管理者最重要的能力和职责之一,而且职级越高就越重要. 很多所谓的"技术债",也就是由一次次的决策失误不断累加而成的. 管理者要能充分利用自己的技 ...
- 云计算时代催生下一代网络变革-软件定义的网络之技术架构篇
我们在基础篇中对SDN的基础概念.核心思想以及市场现状都进行简单地阐述,在本文中将就技术实现方面深入展开讨论. 在ONF于2016年发布的<SDN Architecture Issue 1.1& ...
- Eclipse Theia技术揭秘——脚手架源码分析
在之前的文章中,我们介绍了Theia的构建,其中用到了很多theia的命令,这些命令来自于@theia/cli这个库,本篇文章我们就对@theia/cli以及相关联的库进行分析.本篇文章是继构建桌面I ...
- 搜狐视频P2P技术揭秘 - 分享率控制篇
搜狐视频P2P技术揭秘 - 分享率控制篇 1 业务决定控制逻辑 2 搜狐影音/搜狐视频 2.1 状态定义 2.2 输入事件 2.3 状态转换 2.4 转换逻辑 3 Flash 播放器/H5 播放器 在 ...
- 揭秘高效协作工具背后的技术架构
揭秘高效协作工具背后的技术架构 发表于2015-12-08 10:50| 1731次阅读| 来源CSDN| 4 条评论| 作者蒲婧 CTOCTO俱乐部CTO讲堂管理实践团队协作Worktile wid ...
- .Net微服务实战之技术架构分层篇
一拍即合 上一篇<.Net微服务实战之技术选型篇>,从技术选型角度讲解了微服务实施的中间件的选择与协作,工欲善其事,必先利其器,中间件的选择是作为微服务的基础与开始,也希望给一直想在.Ne ...
最新文章
- 我国第一台微型计算机诞生于哪一年,2015计算机一级《MSOffice》章节练习题及答案(1)...
- java jar包和war包_java中jar包和war包之间有什么区别
- 【Spring框架家族】mybatis generator代码自动生成(看得上眼直接拿去用即可)
- BlockChain:《Blockchain Gate》听课笔记——区块链的共识机制—简介、理解、畅谈
- python 绘图的背景颜色不要_matplotlib自定义添加 “哆啦A梦”背景图,这个操作真牛逼!...
- VBSCRIPT的循环挺好理解的
- 「leetcode」669. 修剪二叉搜索树:【递归】【迭代】详解
- tl-wdr5620千兆版设置虚拟服务器,TL-WDR5620路由器如何设置 TL-WDR5620路由器上网设置步骤【介绍】...
- 图像patch feature源码
- vs2013编译驱动错误-Inf2Cat,signability test failed
- 一个全网最详细 Python 教程,不信你来学一学!
- linux系统下配置无线网卡的方法,linux系统下配置无线网卡的具体步骤
- 每日一题(2022-05-04)——找出游戏的获胜者
- JAVA项目工作经验总结
- 《孩子快抓紧妈妈的手》
- Python_Task06:函数与Lambda表达式
- php 英语自我介绍,工作英文自我介绍
- chrome 无法拖拽离线安装本地CRX格式插件的解决方法
- pubg服务器正在维护中 维护时间请参考,pubg服务器维护中
- 智能交通管理系统顶层设计方案(附下载)
热门文章
- 818专业课【考经】—《信号系统》之章节概要:第三章 连续时间系统的时域分析
- javaweb农产品销售宣传网站
- springboot+dubbo实现项目itrip-search-consumer流程
- 微信小程序源码分享合集-5
- PS人像磨皮调色插件ultimate retouch中文汉化版
- EndNote中英文混排时et al和等的解决方法
- 计算机应用基础本科电大性考,(精)2017年电大计算机应用基础形成性考核册(电大形考参考答案).doc...
- kmeans及模型评估指标_机器学习模型评估指标总结
- 2010全球IT企业市值TOP 10
- 串口屏与6050_重磅来袭串口触摸屏,一个过来人的经验