编者注
在
上一篇文章中,我们讨论了Voximplant控制面板的发布,不要忘了提到更新的IDE。 今天,我们为此工具专门设计了一个
替代品 -我们的同事
Geloosa精心描述了选择技术的过程以及带有选项卡,自动完成和自定义样式的实现。 更方便地坐下,把其余的事情搁置一旁,去索具,摩纳哥的胆量正等待着好奇-不要滑倒,有很多:)喜欢阅读。
为代码编辑器选择哪个库?
Npm为代码编辑器生成400多个结果。 在大多数情况下,它们是为特定框架或项目制作的几个最受欢迎的lib的UI包装器,针对同一lib的插件或对其fork进行了修改的分叉,以及不用于在浏览器中编辑代码的操作,它们只是通过关键字进入输出。 因此,幸运的是,选择范围要窄得多。 还有几个库-轻量级但不是很实用的
CodeFlask库,专为小片段和交互式示例而设计,但不适用于具有我们在桌面编辑器中惯用的功能的完整的Web IDE。
最后,我们有3个库可供选择:
Ace ,
CodeMirror和
Monaco Editor 。 其中最早的CodeMirror是由柏林人
Marijn Haverbeke提出的一项私人计划,他在其在线教程
Eloquent JavaScript中需要一个练习代码编辑器。 编辑器的第一版于2007年发布。 2010年,Ace的第一个版本在同一柏林的JSConf.eu上发布,Ajax.org随后为基于云的IDE Cloud9开发了Ace(实际上,Ace代表Ajax.org Cloud9 Editor)。 2016年,Cloud9被亚马逊收购,现已成为AWS的一部分。 最新的摩纳哥编辑器是VS Code的组件,由Microsoft于2015年底发布。
每个编辑器都有自己的优点和缺点;每个编辑器都用于多个大型项目中。 例如,Chrome和Firefox开发人员工具(Bitbucket中的IDE)在npm中的RunKit中使用CodeMirror。 Ace-MODX可汗学院Codecademy; 摩纳哥-在GitLab IDE和CodeSandbox中。 下面是一个比较表,可以帮助您选择最适合您的项目的库。
| 图书馆 |
| 王牌 | 代码镜像 | 摩纳哥 |
开发者 | Cloud9 IDE(Ajax.org), 现在是AmazonMozilla的一部分 | 玛丽恩·哈弗贝克 | 微软公司 |
浏览器支持 | Firefox ^ 3.5 镀铬 Safari ^ 4.0 IE ^ 8.0 歌剧^ 11.5 | Firefox ^ 3.0 镀铬 Safari ^ 5.2 IE ^ 8.0 歌剧^ 9.2 | Firefox ^ 4.0 镀铬 Safari(v-?) IE ^ 11.0 歌剧^ 15.0 |
语言支持 (语法突出显示) | > 120 | > 100 | > 20 |
中的字符数 的最新版本 cndjs.com | 366608(v1.4.3) | 394,269(v5.44.0) | 2,064,949(v0.16.2) |
最新版本的重量, gzip | 2.147 KB | 1.411 KB | 10.898 KB |
渲染图 | 多姆 | 多姆 | DOM和部分<canvas> (用于滚动和小地图) |
该文件 | 10人中有7人:没有搜索,不一定总是清晰 方法返回,令人怀疑 完整性和相关性 (并非所有链接都在扩展坞中起作用) | 十分之六:与用户指南合并, 通过Ctrl + F搜索, 对完整性存在疑问 | 10人中有9人:很漂亮,而且搜索 交叉参考 -1分,缺乏解释 到某些标志的应用 从名字上不太明显 |
快速入门演示 | 操作方法-带有代码示例的文本文档, 分别有带有代码示例的演示 (是的,它们分散在不同的页面上, 并非每个人都能正常工作,因此最容易通过Google进行搜索), 有一个演示,您可以在其中触摸不同的功能, 但建议通过UI控件进行管理, 也就是说,我们仍然必须分别搜索方法 连接他们 | 方法真的很差 基本上一切都散布在github上 和stackoverflow,但其中有一些带有示例的功能演示 实施代码 | 以游乐场的形式组合: 带有注释和一些演示的代码,您可以 立即尝试并评估 许多可能性 |
社区活动 | 平均值 | 高 | 平均值 |
开发者活动 | 平均值 | 平均值 | 高 |
按大小比较库是没有意义的,因为这完全取决于特定项目的连接方式和连接方式:使用其中一个版本(也有所不同)加载完成的文件,或者通过某种收集器运行npm软件包。 最重要的是使用了多少编辑器:是否加载了所有样式和主题,使用了多少以及哪些附加组件和插件。 例如,在CodeMirror中,开箱即用在Monaco和Ace中可用的大多数功能仅在附加组件中可用。 该表显示了CDN上最新版本中的字符数及其压缩文件的权重,以大致了解所涉及的顺序。
所有库都具有大致相同的基本功能集:代码自动格式化,折叠线,剪切/复制/粘贴,热键,添加用于突出显示和排序的新语法的功能,语法检查(仅在CodeMirror中通过加载项,在Ace中到目前为止仅针对JavaScript) / CoffeeScript / CSS / XQuery),工具提示和自动完成功能(在CodeMirror中-通过附加组件),按代码进行高级搜索(在CodeMirror中-通过附加组件),用于实现制表符和拆分模式的方法,diff模式和合并工具(在CodeMirror中) -在一个窗口中有加号或减号,或者在附加组件中有两个面板, 王牌 - 独立利布)。 由于年代久远,已经为CodeMirror编写了许多附加组件,但是它们的数量将影响编辑器的重量和速度。 摩纳哥可以开箱即用地做很多事情,而且我认为它比Ace和CodeMirror更好,更大。
我们之所以在摩纳哥停留有几个原因:
- 我们认为对项目至关重要的最先进的工具:
- IntelliSense-提示和自动完成;
- 在上下文菜单中和通过小地图进行智能代码导航;
- 开箱即用的两面板差异模式。
- 用TypeScript编写。 我们的控制台使用Vue + Typescript编写,因此TS支持非常重要。 顺便说一下,Ace最近还支持TS,但是它最初是用JS编写的。 对于CodeMirror, DefinitelyTyped中有一些类型 。
- 它是其中最活跃的开发工具(可能是因为它是不久前发布的),错误得到更快地纠正,并且池请求也得到了解决。 为了进行比较,使用CodeMirror时,我们经历了一段悲伤的经历,当时错误多年没有得到纠正,我们将拐杖放在拐杖上,然后开车拐杖。
- 方便的自动生成的文档(为其完整性提供了希望),在接口和方法之间具有交叉引用。
- 以我们的口味,最漂亮的UI(可能还与创建时间有关)和简洁的API。
- 在问过开发人员的朋友之后,哪位编辑最引起头痛,Ace和CodeMirror是领导者。
另外,应该说工作速度。 昂贵的解析在并行工作线程中进行。 另外,所有计算都受到视口大小的限制(所有类型,颜色,渲染仅针对可见线进行计算)。 仅当代码包含100,000行时,它才会开始制动-可以计算出几秒钟的提示。 Ace也使用大量的工人进行大量计算,结果证明速度更快:在相同长度的代码中,提示几乎立即出现,并且可以快速处理200,000行(在官方网站上,即使是400万行也不是问题,尽管螺丝被加速,输入开始变慢,并且提示消失了(一百万之后)。 没有并行计算的CodeMirror几乎无法提取此类卷:它可以闪烁文本和语法突出显示。 由于文件中的100,000行在现实世界中很少见,因此我们对此视而不见。 即使拥有4万到5万行,摩纳哥也做得很好。
连接摩纳哥并使用基本功能(例如,与Vue集成)
连接方式
在这里,我将提供来自vue组件的代码示例,并使用适当的术语。 但是,所有这些都可以轻松移植到任何其他框架或纯JS。
摩纳哥的源代码可以在官方网站上下载并放入您的项目中,您可以从CDN中提取它,也可以通过npm连接到该项目。 我将讨论第三个选项并使用webpack进行构建。
我们放置了monaco-editor和一个用于组装的插件:
npm i -S monaco-editor npm i -D monaco-editor-webpack-plugin
在webpack配置中,添加:
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = {
如果使用Vue和vue-cli-service进行构建,请添加到vue.config.js:
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = {
如果不需要Monaco的所有语言和功能,要减小捆绑包的大小,可以将具有设置
MonacoWebpackPlugin
对象传输到
MonacoWebpackPlugin
:
new MonacoWebpackPlugin({ output: '',
插件的功能和语言的完整列表在
这里 。
创建和自定义编辑器
我们导入
editor
并调用
editor.create(el: HTMLElement, config?: IEditorConstructionOptions)
,将要在其中创建编辑器的DOM元素作为第一个参数传递。
在编辑器组件中:
<template> <div ref='editor' class='editor'></div> </template> <script> import {editor} from 'monaco-editor'; import {Component, Prop, Vue} from 'vue-property-decorator'; @Component() export default class Monaco extends Vue { private editor = null; mounted() { this.editor = editor.create(this.$refs.editor); } } </script> <style> .editor { margin: auto; width: 60vw; height: 200px; } </style>
编辑器的容器必须设置高度,以免高度不为零。 如果您在一个空的div中创建编辑器(高度为零,即K.O.),则Monaco将在编辑器窗口中以内联样式编写相同的高度。
editor.create
的第二个可选参数是编辑器配置。 其中有一百多个选项,有关
IEditorConstructionOptions接口的完整说明,请
参见文档。
例如,我们将设置语言,主题和初始文本并启用换行(默认情况下,它们不被换行):
const config = { value: `function hello() { alert('Hello world!'); }`, language: 'javascript', theme: 'vs-dark', wordWrap: 'on' }; this.editor = editor.create(this.$refs.editor, config);
editor.create
函数返回带有
IStandaloneCodeEditor接口的对象。 通过它,您现在可以控制编辑器中发生的所有事情,包括更改初始设置:
现在让您痛苦的是:
updateOptions
接受带有
IEditorOptions接口的对象,而不是IEditorConstructionOptions。 它们略有不同:IEditorConstructionOptions较宽,它包含此编辑器实例的属性和某些全局实例的属性。
updateOptions
属性通过
updateOptions
全局更改-通过全局
editor
的方法更改。 因此,全局更改的那些实例在所有情况下都会更改。 在这些选项中,
theme
。 创建两个具有不同主题的实例; 两者中的y将是最后一个给出的(此处为深色)。 全局
editor.setTheme('vs')
方法也将同时更改这两个主题。 这甚至会影响SPA另一页上的那些窗口。 这样的地方很少,但是您需要关注它们。
<template> <div ref='editor1' class='editor'></div> <div ref='editor2' class='editor'></div> </template> <script> </script>
删除编辑器
销毁摩纳哥窗口时,必须调用
dispose
方法,否则将不会清除所有侦听器,并且此后创建的窗口可能无法正常工作,对某些事件进行多次响应:
beforeDestroy() { this.editor && this.editor.dispose(); }
标签
在文件编辑器中打开的选项卡使用相同的Monaco窗口。 要在它们之间切换,请使用IStandaloneCodeEditor方法:用于保存的
getModel
和用于更新编辑器模型的
setModel
。 该模型存储文本,光标位置,撤消重做的动作历史记录。 要创建新文件的模型,请使用
editor.createModel(text: string, language: string)
全局方法。 如果文件为空,则无法创建模型并将
null
传递给
setModel
:
查看代码 <template> <div class='tabs'> <div class='tab' v-for="tab in tabs" :key'tab.id' @click='() => switchTab(tab.id)'> {{tab.name}} </div> </div> <div ref='editor' class='editor'></div> </template> <script> import {editor} from 'monaco-editor'; import {Component, Prop, Vue} from 'vue-property-decorator'; @Component() export default class Monaco extends Vue { private editor = null; private tabs: [ {id: 1, name: 'tab 1', text: 'const tab = 1;', model: null, active: true}, {id: 2, name: 'tab 2', text: 'const tab = 2;', model: null, active: false} ]; mounted() { this.editor = editor.create(this.$refs.editor); } private switchTab(id) { const activeTab = this.tabs.find(tab => tab.id === id); if (!activeTab.active) { </script>
差异模式
对于差异模式,创建编辑器窗口时需要使用另一种
editor
方法
createDiffEditor
:
<template> <div ref='diffEditor' class='editor'></div> </template> // ... mounted() { this.diffEditor = editor.createDiffEditor(this.$refs.diffEditor, config); } // ...
它使用与
editor.create
相同的参数,但配置应具有
IDiffEditorConstructionOptions接口,该接口与常规编辑器配置稍有不同,尤其是它没有
value
。 通过
setModel创建返回的
IStandaloneDiffEditor后,设置要比较的文本:
this.diffEditor.setModel({ original: editor.createModel('const a = 1;', 'javascript'), modified: editor.createModel('const a = 2;', 'javascript') });
上下文菜单,命令面板和热键
摩纳哥使用其自己的非浏览器上下文菜单,该菜单具有智能导航,可更改所有事件的多光标以及类似于VS Code(命令面板)的命令面板,其中包含一堆有用的命令和快捷键,可加速代码编写:
摩纳哥上下文菜单
摩纳哥命令面板
上下文菜单通过
addAction
方法扩展(在
IStandaloneCodeEditor
和
IStandaloneDiffEditor
都可用),该
菜单接受
IActionDescriptor对象:
为了仅将快捷方式绑定到操作而不在上下文菜单中显示该快捷方式,使用了相同的方法,仅未指定该操作的
contextMenuGroupId
:
命令面板将包括所有添加的操作。
提示和自动完成
出于这些目的,摩纳哥使用了
IntelliSense ,这很酷。 您可以阅读并在屏幕快照上看到链接,他可以显示多少有用的信息。 如果您的语言还没有自动完成功能,则可以通过
registerCompletionItemProvider添加它。 对于JS和TS,已经有一个
addExtraLib
方法,允许您加载工具提示和自动完成的TypeScript定义:
在第一个参数中,该行传递定义,在第二个可选参数中传递库的名称。
自定义语言和主题
摩纳哥拥有用于确定其语言语法的
Monarch模块。 语法非常标准地描述:设置了该语言特有的常规和标记之间的对应关系。
您还可以为令牌创建主题(具有
IStandaloneThemeData接口的对象)并将其安装在全局
editor
:
现在,所描述语言的文本将如下所示:
只要您有足够的想象力,就可以应用此功能。 例如,我们在开发人员面板中制作了一个呼叫日志查看器。 日志通常很长且难以理解,但是当它们以语法突出显示,智能搜索,折叠/展开线,必要的命令(例如,Prettify params)显示时,按其ID突出显示所有呼叫行或将日志中的时间转换为其他时区,然后进行挖掘在他们中变得容易得多(可单击屏幕快照):
结论
总而言之,我要说摩纳哥是火。 与他一起工作了几个月之后,我有了特别愉快的回忆。 如果您选择代码的编辑器,请确保进入其
Playground并试用该代码,看看它还能做什么。 也许这正是您想要的。