monaco-editor 在vue3中的使用

3,626 阅读1分钟

1. 首先在composables中创建useMonaco.ts

import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js'

export default function useMonaco(language = 'json') {
  let monacoEditor: monaco.editor.IStandaloneCodeEditor | null = null
  let initReadOnly = false
  const updateVal = async (val: string) => {
    monacoEditor?.setValue(val)
    setTimeout(async () => {
      initReadOnly && monacoEditor?.updateOptions({ readOnly: false })
      await monacoEditor?.getAction('editor.action.formatDocument').run()
      initReadOnly && monacoEditor?.updateOptions({ readOnly: true })
    }, 100)
  }

  const createEditor = (el: HTMLElement | null, editorOption: monaco.editor.IStandaloneEditorConstructionOptions = {}) => {
    if (monacoEditor) {
      return
    }
    initReadOnly = !!editorOption.readOnly
    monacoEditor = el && monaco.editor.create(el, {
      language,
      minimap: { enabled: false },
      theme: 'vs-light',
      multiCursorModifier: 'ctrlCmd',
      scrollbar: {
        verticalScrollbarSize: 8,
        horizontalScrollbarSize: 8,
      },
      tabSize: 2,
      automaticLayout: true, // 自适应宽高
      ...editorOption
    })
    return monacoEditor
  }
  const onFormatDoc = () => {
    monacoEditor?.getAction('editor.action.formatDocument').run()
  }
  return {
    updateVal,
    getEditor: () => monacoEditor,
    createEditor,
    onFormatDoc
  }
}

2. 在components中创建MonacoEditor.vue

<template>
  <div class="editor-area" :class="isFull ? 'full' : ''" :style="{ width, height }">
    <div class="tools">
      <ElTooltip placement="right" :content="isFull ? '缩小' : '放大'">
        <div class="expand" @click="isFull = !isFull">
          <i :class="isFull ? 'el-icon-close' : 'el-icon-full-screen'"></i>
        </div>
      </ElTooltip>
      <ElTooltip placement="right" content="格式化">
        <div class="expand" @click="onFormatDoc">
          <i class="el-icon-finished"></i>
        </div>
      </ElTooltip>
    </div>
  </div>
</template>

<script lang='ts'>
import useMonaco from '@/composables/useMonaco'
import { defineComponent, ref } from 'vue'
import { ElTooltip } from 'element-plus'

export default defineComponent({
  components: {
    ElTooltip
  },
  props: {
    width: {
      type: String,
      default: '100%'
    },
    height: {
      type: String,
      default: '90vh'
    },
    language: {
      type: String,
      default: 'json'
    },
    preComment: {
      type: String,
      default: ''
    },
    modelValue: {
      type: String,
      default: ''
    },
    editorOptions: {
      type: Object,
      default: () => ({})
    },
  },
  watch: {
    modelValue(val) {
      val !== this.getEditor()?.getValue() && this.updateMonacoVal(val)
    }
  },
  setup(props) {
    const { updateVal, getEditor, createEditor, onFormatDoc } = useMonaco(props.language)
    const isFull = ref(false)
    return {
      isFull,
      updateVal,
      getEditor,
      createEditor,
      onFormatDoc
    }
  },
  methods: {
    updateMonacoVal(_val?: string) {
      const { modelValue, preComment } = this.$props
      const val = preComment ? `${preComment}\n${_val || modelValue}` : (_val || modelValue)
      this.updateVal(val)
    }
  },
  mounted() {
    if (this.$el) {
      const monacoEditor = this.createEditor(this.$el, this.$props.editorOptions)
      this.updateMonacoVal()
      monacoEditor!.onDidChangeModelContent(() => {
        this.$emit('update:modelValue', monacoEditor!.getValue())
      })
      monacoEditor!.onDidBlurEditorText(() => {
        this.$emit('blur')
      })
    }
  }
})
</script>

<style lang="less" scoped>
.editor-area {
  position: relative;
  border: 1px solid #ddd;
  border-radius: 4px;
  overflow: hidden;
  padding: 5px;
  padding-left: 0;
  background-color: #fff;
  box-sizing: border-box;
  &.full {
    position: fixed;
    left: calc(10vw / 2);
    top: calc(10vh / 2);
    box-shadow: 0 0 22px 10px rgba(0, 0, 0, .3);
    width: 90vw!important;
    height: 90vh!important;
    z-index: 9999;
  }
  .tools {
    z-index: 888;
    position: absolute;
    display: flex;
    flex-direction: column;
    height: 100%;
    padding: 0 2px;
    border-right: 1px solid rgba(0, 0, 0, .1);
    left: 0;
    bottom: 0px;
    top: 0;
    .expand {
      cursor: pointer;
      line-height: 0;
      margin-top: 5px;
    }
  }
}
</style>

3. 在需要用到MonacoEditor的文件中直接引入即可

<MonacoEditor
    height="100px"
    v-model="editorData" 
    language="json"
/>