持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情
是不是觉得在线代码编辑器很复杂和高大上,其实它就是一个简单的文本编辑器,只不过会添加一些运行环境和语法高亮而已,所以我们可以自己写一个在线代码编辑器,只需要一个文本编辑器和一个运行环境就可以了。
先来看一下截图的效果,想先体验效果的可以直接把文章拉到最底下:
1. 构思
我们的在线代码编辑器需要有以下功能:
- 编辑
html、css、js代码 - 预览效果
- 语法高亮
在线运行在浏览器中,所以我们需要一个文本编辑器,一个运行环境,一个语法高亮插件,一个预览功能。
2. 实现
我们可以通过codemirror来实现一个文本编辑器,这个库是一个开源的文本编辑器,可以实现语法高亮,代码折叠等功能,我们只需要引入它的css和js文件就可以了。
我们这里只需要实现css、html和js的编辑,所以我们只需要按需安装下面的插件:
codemirror:文本编辑器@codemirror/lang-html:html语法高亮@codemirror/lang-css:css语法高亮@codemirror/lang-javascript:js语法高亮
2.1 初始化
这里我使用的是vue-cli3,先初始化一个项目:
vue create online-editor
然后安装上面的库:
npm i codemirror @codemirror/lang-html @codemirror/lang-css @codemirror/lang-javascript
现在开发环境已经搭建好了,接下来就可以开始编写代码了。
2.2 编辑器
使用CodeMirror创建一个编辑器,创建的方法很简单,先来实验一下功能:
<template>
<div id="app">
<div id="editor"></div>
</div>
</template>
<script>
import {EditorView, basicSetup} from 'codemirror'
import {javascript} from '@codemirror/lang-javascript'
export default {
name: 'App',
mounted() {
new EditorView({
extensions: [
basicSetup,
javascript()
],
parent: document.getElementById("editor"),
})
}
}
</script>
就在App.vue中添加上面的代码,然后运行npm run serve,就可以看到一个简单的编辑器了,可以在里面输入javascript代码(这里不限制代码类型,只是上面安装了javascript高亮和代码提示的插件,所以js代码会高亮显示还有一些代码提示),然后它会自动高亮。
2.3 获取编辑器内容
可以通过EditorView的state属性来获取编辑器的内容,它是一个EditorState对象,可以通过doc
属性来获取文档内容,然后通过toString方法来获取文档内容。
const editor = new EditorView({
extensions: [
basicSetup,
javascript()
],
parent: document.getElementById("editor"),
})
const content = editor.state.doc.toString()
这里我没用找到EditorView的change事件,翻遍了文档也没找到,百度了也没找到,如果有这个需求可以通过定时器来获取内容。
网上还有封装好了的
vue-codemirror组件,codemirror这个库我看文档都是基于传统的html页面来使用的,通过cdn引入,本质上是同源的,我只是在vue项目中练练手。
3. 正式实现
3.1 布局
参考其他的在线编辑器,可以把编辑器分为四个模块,分为上下两个区域,上面的区域是编辑器,下面的区域是预览区域,先来实现布局:
<template>
<div id="app">
<button>运行</button>
<div class="editor-container">
<div id="htmlEditor">
<h3>html</h3>
</div>
<div id="cssEditor">
<h3>css</h3>
</div>
<div id="jsEditor">
<h3>javascript</h3>
</div>
</div>
<div>
<h3>result</h3>
<div id="result"></div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {}
},
}
</script>
<style scoped>
.editor-container {
display: flex;
flex-direction: row;
height: 50vh;
}
#cssEditor,
#htmlEditor,
#jsEditor {
width: 33.33%;
flex-shrink: 0;
border: 1px solid #ccc;
}
h3 {
margin: 0;
padding: 10px;
background-color: #eee;
border-bottom: 1px solid #ccc;
}
</style>
布局就这么简单,先把编辑器的布局实现了,然后上面加了一个按钮,点击按钮就可以运行代码。
3.2 初始化编辑器
根据上面讲到过的知识点,初始化不就是so easy了吗,只需要在mounted钩子中初始化编辑器就可以了,然后把编辑器的dom元素传入到EditorView的parent属性中,这样编辑器就会渲染到dom元素中。
mounted() {
this.htmlEditor = new EditorView({
extensions: [
basicSetup,
html()
],
parent: document.getElementById("htmlEditor"),
})
this.cssEditor = new EditorView({
extensions: [
basicSetup,
css()
],
parent: document.getElementById("cssEditor"),
})
this.jsEditor = new EditorView({
extensions: [
basicSetup,
javascript()
],
parent: document.getElementById("jsEditor"),
})
}
上面的代码虽然没有注释,但是也不需要我解释了吧,简简单单嘛。
3.3 运行代码
重头戏就是上面点击运行按钮的时候,把编辑器中的代码拿出来,然后拼接成一个完整的html页面,然后把这个页面渲染到result区域中,这里可以使用iframe来实现。
所以这里的重点就是点击按钮的事件:
handleRun() {
// 获取编辑器中的代码
const htmlCode = this.htmlEditor.state.doc.toString()
const cssCode = this.cssEditor.state.doc.toString()
const jsCode = this.jsEditor.state.doc.toString()
// 清空result区域
const result = document.getElementById("result")
result.innerHTML = ""
// 创建iframe,设置一丢丢样式
const iframe = document.createElement("iframe")
iframe.style.width = "100%"
iframe.style.height = "100%"
iframe.style.border = "none"
result.appendChild(iframe)
// 这里我们需要等待iframe加载完成,然后才能往里面写代码,所以这里直接使用 setTimeout 0
setTimeout(() => {
// 获取iframe的document,并写入html代码
const doc = iframe.contentDocument
doc.open()
doc.write(htmlCode)
doc.close()
// 创建style标签,写入css代码
const style = doc.createElement("style")
style.innerHTML = cssCode
doc.head.appendChild(style)
// 创建script标签,写入js代码
const script = doc.createElement("script")
script.innerHTML = jsCode
doc.body.appendChild(script)
}, 0)
}
简简单单我们就完成了一个在线代码编辑器,让我们来看看码上掘金中的码上掘金:
4. 总结
没想到我们使用了这么一丢丢代码就能写一个在线代码编辑器,跟着我来试试,写完了肯定会成就感满满呀。
codemirror是一个非常强大的代码编辑器,它还有很多其他的功能,比如设置主题、设置语言、设置字体、设置字号等等,这些功能都是通过extension来实现的,所以我们可以根据自己的需求来扩展codemirror,这里就不一一介绍了,感兴趣的可以自己去看看文档。