写一个在线代码编辑器吧,在码上掘金里再创建一个码上掘金

467 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情

是不是觉得在线代码编辑器很复杂和高大上,其实它就是一个简单的文本编辑器,只不过会添加一些运行环境和语法高亮而已,所以我们可以自己写一个在线代码编辑器,只需要一个文本编辑器和一个运行环境就可以了。

先来看一下截图的效果,想先体验效果的可以直接把文章拉到最底下:

image.png

1. 构思

我们的在线代码编辑器需要有以下功能:

  • 编辑htmlcssjs代码
  • 预览效果
  • 语法高亮

在线运行在浏览器中,所以我们需要一个文本编辑器,一个运行环境,一个语法高亮插件,一个预览功能。

2. 实现

我们可以通过codemirror来实现一个文本编辑器,这个库是一个开源的文本编辑器,可以实现语法高亮,代码折叠等功能,我们只需要引入它的cssjs文件就可以了。

我们这里只需要实现csshtmljs的编辑,所以我们只需要按需安装下面的插件:

  • 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 获取编辑器内容

可以通过EditorViewstate属性来获取编辑器的内容,它是一个EditorState对象,可以通过doc 属性来获取文档内容,然后通过toString方法来获取文档内容。

const editor = new EditorView({
    extensions: [
        basicSetup,
        javascript()
    ],
    parent: document.getElementById("editor"),
})

const content = editor.state.doc.toString()

这里我没用找到EditorViewchange事件,翻遍了文档也没找到,百度了也没找到,如果有这个需求可以通过定时器来获取内容。

网上还有封装好了的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元素传入到EditorViewparent属性中,这样编辑器就会渲染到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,这里就不一一介绍了,感兴趣的可以自己去看看文档。

5. 参考

CodeMirror