首页 前端知识 vue项目中,在线编辑器vue-codemirror、ace、和monaco的使用及踩坑记录

vue项目中,在线编辑器vue-codemirror、ace、和monaco的使用及踩坑记录

2024-11-05 23:11:59 前端知识 前端哥 193 401 我要收藏

由于第一次使用代码编辑器插件,之前都没接触过,花了几天时间才做出来满意的,参考了好几个大佬的文章,大家有需要可以自行查找相关文章!另外文底也献上了个别的参考文章。

一、vue-codemirror编辑器

vue2版本

vue2版本不支持最新的vue-codemirror,所以要下载固定的版本4.0.6,并且要搭配codemirror使用,codemirror也不建议使用6以上的版本

pnpm add codemirror@5.65.12 vue-codemirror@4.0.6

然后在main.js中全局挂载vuecodemirror

//codemirror
import VueCodemirror from 'vue-codemirror'
import 'codemirror/lib/codemirror.css'
Vue.use(VueCodemirror)

在要展示codemirror的页面,导入以下文件,包括有语言文件JavaScript,主体样式eclipse,和代码折叠所需的文件fold/xxx

import { codemirror } from 'vue-codemirror'
import 'codemirror/theme/eclipse.css'
import "codemirror/mode/javascript/javascript.js";
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/fold/foldcode'
import 'codemirror/addon/fold/foldgutter.js'
import 'codemirror/addon/fold/xml-fold'
import 'codemirror/addon/fold/brace-fold'
import 'codemirror/addon/fold/indent-fold'
import "codemirror/addon/hint/javascript-hint.js";
import "codemirror/addon/selection/active-line.js"

需要更改语言或者主体样式的,可以再node_modules里面找到相应文件import导入使用即可

添加配置项,这里存在一个bug就是自动补全括号无法实现,有看到的友友成功实现的话,可以评论区踢一下我

data () {
    return {
      curCode: '',
      cmOptions: {
        value: this.curCode,
        autorefresh: true,
        tabSize: 4, //tab空格宽度
        mode: 'text/javascript',
        line: true,
        viewportMargin: Infinity, //处理高度自适应时搭配使用
        highlightDifferences: true,
        spellcheck: true,
        autofocus: true,
        indentWithTab: true,
        smartIndent: true,
        styleActiveLine: true, // 设置光标所在行高亮
        showCursorWhenSelecting: true,
        lineNumbers: true, // 显示行号
        // matchBrackets: true, //自动补全括号
        // autoCloseBrackets: true,
        foldGutter: true, //代码折叠
        gutters: ["CodeMirror-linenumbers", 'CodeMirror-foldgutter'], 
        theme: "eclipse",
      },
      
    }
  },

最后就是使用了,大家也可以自行设置需要的样式

<div style="width: 600px;height: 300px; border: 1px solid #e5e5e5;" >
    <codemirror ref="mycode" 
      v-model="curCode" 
      :options="cmOptions" 
      class="code" 
      style="width: 100%;height: 100%;"
      @change="handleEdit"
    > 
    </codemirror>
  </div>

附上全部源码

<template>
  <div style="width: 600px;height: 300px; border: 1px solid #e5e5e5;" >
    <codemirror ref="mycode" 
      v-model="curCode" 
      :options="cmOptions" 
      class="code" 
      style="width: 100%;height: 100%;"
      @change="handleEdit"
    > 
    </codemirror>
  </div>
</template>
 
<script>
import { codemirror } from 'vue-codemirror'
import 'codemirror/theme/eclipse.css'
import "codemirror/mode/javascript/javascript.js";
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/fold/foldcode'
import 'codemirror/addon/fold/foldgutter.js'
import 'codemirror/addon/fold/xml-fold'
import 'codemirror/addon/fold/brace-fold'
import 'codemirror/addon/fold/indent-fold'
import "codemirror/addon/hint/javascript-hint.js";
import "codemirror/addon/selection/active-line.js"

export default {
  name: "HelloWord",
  components: {codemirror},
  data () {
    return {
      curCode: '',
      cmOptions: {
        value: this.curCode,
        autorefresh: true,
        tabSize: 4, //tab空格宽度
        mode: 'text/javascript',
        line: true,
        viewportMargin: Infinity, //处理高度自适应时搭配使用
        highlightDifferences: true,
        spellcheck: true,
        autofocus: true,
        indentWithTab: true,
        smartIndent: true,
        styleActiveLine: true, // 设置光标所在行高亮
        showCursorWhenSelecting: true,
        lineNumbers: true, // 显示行号
        // matchBrackets: true, //自动补全括号
        // autoCloseBrackets: true,
        foldGutter: true,
        gutters: ["CodeMirror-linenumbers", 'CodeMirror-foldgutter'], 
        theme: "eclipse",
      },
      
    }
  },
  methods: {
    handleEdit() {
      console.log('44');
    },
  }
}
</script>
 
<style>
  .code {
    text-align: left;
    font-size: 16px;
    font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;
  }
  
</style>

效果图如下:

vue3版本

需要下载最新版本的vue-codemirror和codemirror

"vue-codemirror": "^6.1.1",

"codemirror": "^6.0.1",

紧接着下载语言包和主题包

# CodeMirror languages...
pnpm add @codemirror/lang-html
pnpm add @codemirror/lang-json
pnpm add @codemirror/lang-javascript

# CodeMirror themes...
pnpm add @codemirror/theme-one-dark

页面中使用的全部代码

<template>
  <codemirror
    v-model="code"
    placeholder="Code goes here..."
    :style="{ height: '400px' }"
    :autofocus="true"
    :indent-with-tab="true"
    :tab-size="2"
    :extensions="extensions"
    @ready="handleReady"
    @change="log('change', $event)"
    @focus="log('focus', $event)"
    @blur="log('blur', $event)"
  />
</template>

<script>
  import { defineComponent, ref, shallowRef } from 'vue'
  import { Codemirror } from 'vue-codemirror'
  import { javascript } from '@codemirror/lang-javascript'
  import { oneDark } from '@codemirror/theme-one-dark'

  export default defineComponent({
    components: {
      Codemirror
    },
    setup() {
      const code = ref(`console.log('Hello, world!')`)
      const extensions = [javascript(), oneDark]

      // Codemirror EditorView instance ref
      const view = shallowRef()
      const handleReady = (payload) => {
        view.value = payload.view
      }

      // Status is available at all times via Codemirror EditorView
      const getCodemirrorStates = () => {
        const state = view.value.state
        const ranges = state.selection.ranges
        const selected = ranges.reduce((r, range) => r + range.to - range.from, 0)
        const cursor = ranges[0].anchor
        const length = state.doc.length
        const lines = state.doc.lines
        // more state info ...
        // return ...
      }

      return {
        code,
        extensions,
        handleReady,
        log: console.log
      }
    }
  })
</script>

在main.js全局中挂载并设置配置项

import { basicSetup } from 'codemirror'
import VueCodemirror from 'vue-codemirror'

const app = createApp()

app.use(VueCodemirror, {
  // optional default global options
  autofocus: true,
  disabled: false,
  indentWithTab: true,
  tabSize: 2,
  placeholder: 'Code goes here...',
  extensions: [basicSetup]
  // ...
})

想要白色背景的codemirror,只要去掉oneDark主题就可以了,导入这个主题样式就是黑色背景

效果图如下:

还发现一个bug就是,在编辑状态的时候会自动添加一层虚线框,找了好久也没发现有设置样式,最后发现overflow:hidden可以隐藏掉虚线框,真是被自己蠢到了~

二、aceEditor编辑器

下载:

pnpm add ace-builds

代码:参考的一个大佬的,但是文章被关掉了,大家可以自己去找找


<template>
  <div class="ace">
    <div ref="ace" class="ace-editor"/>
  </div>
</template>

<script>
//ace-editor
import ace from 'ace-builds'
// ace主题包
import 'ace-builds/src-min-noconflict/theme-eclipse'
// ace 检索框
import 'ace-builds/src-min-noconflict/ext-searchbox'
// ace语言包
import 'ace-builds/src-min-noconflict/mode-javascript'
import 'ace-builds/src-min-noconflict/mode-json5'
//代码完成
import 'ace-builds/src-min-noconflict/ext-language_tools'

export default {
  name: 'AceDemo',
  data() {
    return {
      editor: null
    }
  },
  mounted() {
    ace.config.set("basePath", "https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.14/");
    //快速开始-demo
    this.editor = ace.edit(this.$refs.ace, {
      maxLines: 17, // 最大行数,超过会自动出现滚动条
      // minLines: 1, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小
      fontSize: 14, // 编辑器内字体大小
      theme: 'ace/theme/eclipse', // 默认设置的主题
      mode: 'ace/mode/javascript', // 默认设置的语言模式
      tabSize: 4,// 制表符设置为 4 个空格大小
      readOnly: false, //只读
      useWorker: false, //取消语法检测
    });
  },
  methods: {
    // 填充值
    fillValue: function () {
      this.editor.setValue("hello world", -1)
    },
    getLineNum: function () {
      let lineNum = this.editor.session.getLength();
      alert("总行数为:" + lineNum)
    },
    getLineAndRow: function () {
      let cursor = this.editor.selection.getCursor();
      alert("当前光标所在行列:" + JSON.stringify(cursor))
    },
    // 光标跳转到第几行第几列
    gotoLine: function () {
      this.editor.gotoLine(1, 1);
    },
  }
}
</script>

<style scoped>
.ace {
  width: 100%;
  height: 300px;
  border: 1px solid #ddd;
  overflow: hidden;
  display: flex;
}
.ace-editor {
  width: 100%;
  height: 100%;
  margin-bottom: 20px;
}

.ace-toolbar {
  display: flex;
  justify-content: center;
  margin-top: 20px;
}

.ace-toolbar > button {
  margin-left: 20px;
}

:deep(.ace-eclipse .ace_gutter) {
  background-color: #fff;
  border-right: 0;
}

:deep(.ace_gutter-active-line) {
  background-color: #fff;
}
</style>

效果图如下:

三、monaco编辑器

这个插件和前面两种略有不同,右边会有一个小的回显,可以点击、拖动,类似vscode

下载:这两个插件有版本对应要求,可以自行去查询

"monaco-editor": "^0.31.0",

"monaco-editor-webpack-plugin": "^7.1.0",

在vue.config.js中引入monaco-editor-webpack-plugin,然后添加配置项

const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = defineConfig({
  transpileDependencies: true,
  configureWebpack: {
    plugins: [
      new MonacoWebpackPlugin(),
    ],
  }
})

代码:

这里使用了自定义语言,如果有需要使用官方语言如javascript、python等,把自定义语言开始到结束的部分去掉,然后将language语言支持换成javascript就可以啦。

<template>
  <div id="editor" style="height: 300px;"></div>
</template>

<script setup>
import * as monaco from 'monaco-editor'
import { ref, toRaw, nextTick } from 'vue'

const editor = ref(null)

// 自定义语言开始
monaco.languages.register({ id: 'mySpecialLanguage' });

// 给自定义语言添加自动补全括号
monaco.languages.setLanguageConfiguration('mySpecialLanguage', {
  comments: {
    lineComment: '//',
    blockComment: ['/*', '*/']
  },
  brackets: [['{', '}'], ['[', ']'], ['(', ')']],
  autoClosingPairs: [
    { open: '{', close: '}' },
    { open: '[', close: ']' },
    { open: '(', close: ')' },
    { open: '"', close: '"', notIn: ["string"] },
    { open: "'", close: "'", notIn: ["string", "comment"] },
    { open: "`", close: "`", notIn: ["string", "comment"] },
    { open: "/**", close: " */", notIn: ["string"] },
  ],
  surroundingPairs: [
    { open: '{', close: '}' },
    { open: '[', close: ']' },
    { open: '(', close: ')' }
  ]
});
// 给自定义语言添加匹配规则
monaco.languages.setMonarchTokensProvider('mySpecialLanguage', {
  tokenizer: {
    root: [
      [/\bif\b/, 'keyword'],
      [/\b\d+\b/, 'number'],
      [/\b(a|an|the)\b/, 'article'],
      [/[a-z][a-z0-9]*/, 'identifier'],
      [/[A-Z][a-z0-9]*/, 'type.identifier'],
      [/'([^'\\]|\\.)*'/, 'string'], 
      [/"/, 'string', '@string']
    ],
    string: [
      [/[^\\"]+/, 'string'],
      [/\\./, 'string.escape'],
      [/"/, 'string', '@pop']
    ]
  }
});
// 自定义语言结束

const initEditor = () => {
  editor.value = monaco.editor.create(document.querySelector('#editor'), {
    value: '', //编辑器初始显示文字
    language: 'mySpecialLanguage', //语言支持
    theme: 'vs', //主题
    selectOnLineNumbers: true,//显示行号
    roundedSelection: false,
    readOnly: false, // 只读
    cursorStyle: 'line', //光标样式
    automaticLayout: true, //自动布局
    glyphMargin: true, //字形边缘
    useTabStops: false,
    fontSize: 18, //字体大小
    autoIndent: true, //自动布局
    quickSuggestionsDelay: 100, //代码提示延时
  });
  
  // 监听值的变化
  // editor.value.onDidChangeModelContent((val) => {
  // 	console.log(val.changes[0].text)
  // })
}
nextTick(() => {
  initEditor()
})

// 获取编辑器中的文本
const getEditorValue = () => {
  const value = toRaw(editor.value).getValue()
  // console.log(value);
  
}
// 修改编辑器的文本
const setEditorValue = (val) => {
  toRaw(editor.value).getModel().setValue(val)
}

</script>

效果图如下:

参考的大佬文章:

https://github.com/surmon-china/vue-codemirror

https://blog.csdn.net/weixin_43977534/article/details/123822431

https://www.cnblogs.com/wangjiahui/p/15524189.html

好啦,本人有点懒,文章写得可能还不够详细。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/20077.html
标签
评论
会员中心 联系我 留言建议 回顶部
复制成功!