作为一名coder,像VS Code这样的代码编辑器自然是必不可少的,你还可以使用类似CodeSandbox这样的online编辑器开发demo程序。编辑器更多是作为工具方便我们进行日常的代码开发工作,倘若将编辑器视作产品的一部分为其提供可扩展的能力,我们该如何应对。答案是:web编辑器,现在热门的可视化页面搭建系统便是一个典型的案例。
今天要介绍的是三款开源的主流web编辑器:
Ace
CodeMirror
Monaco Editor
前世今生
Ace是来自Ajax.org Cloud9 Editor的一个独立的代码编辑器,它的前身是Bespin和后来的Skywriter。这两者最开始走的路线不一样,Bespin基于canvas,Ace基于DOM。Ace发布于2010年,之后Skywriter团队将Skywriter的插件系统和可扩展性融合到了Ace中,便形成了现在的Ace编辑器。现在,Ajax.org和Mozilla都在积极的开发和维护Ace。
CodeMirror的第一版于2007年发布,该版本基于浏览器的contentEditable属性实现。2010年发布的Ace采用了新的技术并证明即使是使用javascript操作数以千行的DOM也不存在性能问题,这驱使了CodeMirror的重构并发布了第二个版本,弃用了之前的contentEditable,性能得到了很大提升。如今,CodeMirror即将发布最新的重构版本6。
Monaco Editor算是后起之秀,随着2015年VS Code的发布而诞生,它与VS Code使用同样的核心代码。
快速开始
这里以不引入任何框架的,纯粹的html+css+js形式展示三类编辑器的使用。
Ace
官方示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Editor</title>
<style type="text/css" media="screen">
body {
overflow: hidden;
}
#editor {
margin: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
</style>
</head>
<body>
<pre id="editor">function foo(items) {
var i;
for (i = 0; i < items.length; i++) {
alert("Ace Rocks " + items[i]);
}
}</pre>
<script src="src-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
var editor = ace.edit("editor");
editor.setTheme("ace/theme/twilight");
editor.session.setMode("ace/mode/javascript");
</script>
</body>
</html>
这里承载编辑器内容的html标签是pre,实际开发中并不常见,可以替换成div。
注意ace的引用方式。ace-builds repository是ace的最新发布包,直接将src* 子目录拷贝到项目中即可使用,发布包总共有四个版本。
- src (完整版本)
- src-min (压缩版本)
- src-noconflict (ace.require完整版本)
- src-min-noconflict (ace.require压缩版本)
当然我们也可以自行打包,从github仓库拉取源码,执行下面的脚本。
npm install
node ./Makefile.dryice.js
于是就得到了完整的版本,即上述四个版本中的第一个,还可以通过脚本选项生成另外三个版本或是指定输出目录。
CodeMirror
直接下载zip文件并拷贝到项目中即可使用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="codemirror/lib/codemirror.css" />
<script src="codemirror/lib/codemirror.js"></script>
<script src="codemirror/mode/javascript/javascript.js"></script>
<style>
.CodeMirror {
border: 1px solid black;
}
</style>
</head>
<body>
<div>
<textarea id="editor"></textarea>
<div id="editor2"></div>
</div>
<script>
// 方式1
var ele = document.getElementById("editor");
var editor = CodeMirror.fromTextArea(ele, {
lineNumbers: true,
mode: "javascript"
});
editor.setValue("var a = 'hello world';")
// 方式2
var editor2 = CodeMirror(document.getElementById("editor2"), {
lineNumbers: true,
value: "console.log('hello world');",
mode: "javascript"
})
</script>
</body>
</html>
这里有两种方式创建编辑器,fromTextArea的方式具有一些额外的特性。详情戳这里
Monaco Editor
通过npm安装
npm install monaco-editor
安装完成后生成三个版本。
- esm (es模块化版本,兼容webpack)
- dev (完整的amd模块化版本)
- min (压缩的amd模块化版本)
另外还有一个min版本的source maps文件夹和一个编辑器api描述文件monaco.d.ts
AMD版本的官方示例
<!DOCTYPE html>
<html>
<head>
<title>browser-amd-editor</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>
<body>
<h2>Monaco Editor Sample</h2>
<div
id="container"
style="width: 800px; height: 600px; border: 1px solid grey"
></div>
<!-- OR ANY OTHER AMD LOADER HERE INSTEAD OF loader.js -->
<script src="../node_modules/monaco-editor/min/vs/loader.js"></script>
<script>
require.config({ paths: { vs: "../node_modules/monaco-editor/min/vs" } });
require(["vs/editor/editor.main"], function () {
var editor = monaco.editor.create(
document.getElementById("container"),
{
value: [
"function x() {",
'\tconsole.log("Hello world!");',
"}",
].join("\n"),
language: "javascript",
}
);
});
</script>
</body>
</html>
实际项目基本都是基于前端框架开发,下面就以Vue为例介绍三类编辑器的使用。
在Vue项目中的使用
Ace
- 安装
npm install ace-builds
- 引入
import ace from "ace-builds"
实际使用过程中我们一般都会指定代码的语言类型,还有可能修改编辑器的默认样式。这样的话,我们还需要引入相关的语言类型文件和主题文件。
import "ace-builds/src-noconflict/mode-javascript.js"
import "ace-builds/src-noconflict/theme-tomorrow.js"
但是,如果我们想动态切换语言类型或是主题,是不是应该把对应的js文件全部引入呢?Ace为我们提供了更简洁的方法。
import "ace-builds/webpack-resolver"
这样就完成了语言类型和主题的动态加载,前提是项目基于webpack构建的。
- 常用api
设置主题
myEditor.setTheme("ace/theme/tomorrow")
设置语言类型
// 一个editor可能存在多个session
myEditor.session.setMode("ace/mode/javascript")
myEditor.setMode("ace/mode/javascript")
设置/获取值
myEditor.setValue("the new text here")
myEditor.session.setValue("the new text here")
myEditor.getValue() // or session.getValue
设置tab大小
myEditor.session.setTabSize(4)
是否只读
myEditor.setReadOnly(true) // false可编辑
当我们要设置多个属性值时,除了单独调用每个api,还可以使用下面这种方式。
myEditor.setOptions({
mode: "ace/mode/javascript",
theme: "ace/theme/tomorrow",
value: "hello world"
})
有了这些常用的api,编辑器基本成型了。可以参考官网或是源码ace.d.ts查看全部的接口。
- 高级特性
Ace自带语法检查功能,目前支持JavaScript, JSON, php, CoffeeScript, CSS, XQuery,XML,HTML。
效果如下:
除了语法检查,还可以设置代码提示和自动补全。只需要引入语言构建扩展,并设置相关属性即可。
import "ace-builds/src-noconflict/ext-language_tools"
myEditor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true,
});
效果如下:
图中的提示及补全片段都可以在源码中找到,我们可以按照源码中的语法添加自定义的提示及补全片段信息。
CodeMirror
- 安装
npm install codemirror
- 引入
import CodeMirror from "codemirror/lib/codemirror.js"
import "codemirror/lib/codemirror.css"
同样,对于主题和语言类型也需要引入相应的文件。
import "codemirror/theme/material.css"
import "codemirror/mode/javascript/javascript.js"
- 常用api
设置属性
myEditor.setOption("mode", "text/javascript")
myEditor.setOption("value", "hello world")
设置/获取值
myEditor.getValue()
myEditor.setValue("hello world")
- 高级特性
CodeMirror默认是没有语法检查功能的,需要利用插件addon进行扩展。
import "codemirror/addon/lint/lint.css"
import "codemirror/addon/lint/lint.js"
import "codemirror/addon/lint/javascript-lint.js"
this.editor = CodeMirror.fromTextArea(this.$refs.editor, {
mode: "text/javascript",
gutters: ["CodeMirror-lint-markers"],
lint: true
})
另外还需要引入语言对应的检查工具,可以在项目的index.html中引入。
<script src="https://unpkg.com/jshint@2.9.6/dist/jshint.js"></script>
效果如下。
代码提示和智能补全,需要引入相关插件,设置属性并绑定快捷键触发。
import "codemirror/addon/hint/show-hint.js"
import "codemirror/addon/hint/show-hint.css"
import "codemirror/addon/hint/javascript-hint.js"
myEditor = CodeMirror.fromTextArea(this.$refs.editor, {
mode: "text/javascript",
extraKeys: { "Ctrl-Enter": "autocomplete", "Cmd-Enter": "autocomplete" }
})
CodeMirror.commands.autocomplete = function (cm) {
cm.showHint({ hint: CodeMirror.hint.javascript });
}
这里分别定义了windows和mac系统下触发代码提示和智能补全的组合键,Ctrl+Enter Cmd+Enter。效果如下。
CodeMirror支持diff模式,需要引入的插件及实现如下。
import "codemirror/addon/merge/merge.css"
import "codemirror/addon/merge/merge.js"
const orig1 = `import Vue from 'vue'
import App from './App.vue'\n
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')`;
const orig2 = `import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
Vue.config.productionTip = false;
Vue.use(ElementUI);
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
tabName: "",
},
mutations: {
changeTab(state, tabName) {
state.tabName = tabName;
},
},
});
new Vue({
render: (h) => h(App),
store: store,
}).$mount("#app");`;
myDiffEditor = CodeMirror.MergeView(
document.getElementById("view"),
{
value: orig1,
origLeft: null,
orig: orig2,
lineNumbers: true,
mode: "text/javascript",
highlightDifferences: true,
connect: true,
collapseIdentical: false
}
);
diff功能需要依赖diff-match-patch开源库来计算差异。
<script src="https://cdnjs.cloudflare.com/ajax/libs/diff_match_patch/20121119/diff_match_patch.js"></script>
最终效果如下。
Monaco Editor
- 安装
npm install monaco-editor
此外,还需要安装配套的webpack插件。
npm install monaco-editor-webpack-plugin
在vue.config.js中添加插件。
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
configureWebpack: {
plugins: [
new MonacoWebpackPlugin()
]
}
}
- 引入
import * as monaco from "monaco-editor"
- 常用api
设置语言类型
const model = myEditor.getModel()
monaco.editor.setModelLanuage(model, "javascript")
设置主题
monaco editor自带三种主题,默认的"vs"及"vs-dark"、"hc-black"。
monaco.editor.setTheme("vs-dark")
设置属性
例如,readOnly(是否只读)、renderLineHighlight(高亮行)、lineNumbers(是否显示行号)、fontSize(字体大小)等。
myEditor.updateOptions({
[name]: value // name表示属性名称,value为对应的属性值
})
这里仅列举了部分属性,完整的请参考官网或是源码monaco.d.ts。官网还提供了丰富的示例。
- 高级特性
monaco editor默认支持TypeScript, JavaScript, CSS, LESS, SCSS, JSON, HTML的语法校验及智能提示补全功能。效果如下。
和桌面端的vscode没有任何区别。
再看diff模式。
const originalModel = monaco.editor.createModel(
`import Vue from 'vue'
import App from './App.vue'\n
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')`,
"javascript"
);
const modifiedModel = monaco.editor.createModel(
`import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
Vue.config.productionTip = false;
Vue.use(ElementUI);
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
tabName: "",
},
mutations: {
changeTab(state, tabName) {
state.tabName = tabName;
},
},
});
new Vue({
render: (h) => h(App),
store: store,
}).$mount("#app");`,
"javascript"
);
myDiffEditor = monaco.editor.createDiffEditor(
document.getElementById("monaco-editor")
);
myDiffEditor.setModel({
original: originalModel,
modified: modifiedModel,
});
效果如下。
总结
Ace,CodeMirror,Monaco Editor这三类编辑器的基本功能几乎相差无几,部分api高度相似,同样都具备部分语言的语法校验和代码智能提示及补全功能,只不过在实现方式上有所区别。自定义主题和语言,三者也都支持,实际项目遇到的可能性较小,文中没有介绍。Ace和CodeMirror的diff模式都依赖第三方开源库,而Monaco Editor自带diff功能。Monaco Editor与vscode同根同源,对于日常的vscode使用者,monaco editor相比之下无疑更加亲切,UI更加美观。CodeMirror重构之后的新版本同样值得期待。如果项目需要用到TypeScript,Ace和Monaco Editor可能是更好的选择。
最后附上编辑器demo的github链接,文中的代码片段均来自于此。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!