monaco-editor是微软的项目,但使用npm包方式,文件太大,导致项目会卡顿,所以使用了esm方式,话不多说直接上代码

<template>
  <div ref="codeEditBox" class="code-edit-container"> </div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, onBeforeUnmount, watch } from 'vue'
import { editorProps } from './monacoEditorType/index'

export default defineComponent({
  name: 'MonacoEditor',
  props: editorProps,
  emits: ['update:modelValue', 'change', 'editor-mounted'],
  setup(props, { emit }) {
    let editor: any
    const codeEditBox = ref()
    let monacoInited = false
    const loadScriptResultMap = {}

    // Script引入地址方法
    const importScript = src => {
      if (loadScriptResultMap[src]) {
        return loadScriptResultMap[src]
      }
      loadScriptResultMap[src] = new Promise((resolve, reject) => {
        const newScript = document.createElement('script')
        newScript.src = src
        document.body.append(newScript)
        newScript.addEventListener('load', () => {
          resolve(true)
        })
        newScript.onerror = () => {
          monacoInited = false
          document.body.removeChild(newScript)
          reject()
        }
      })
      return loadScriptResultMap[src]
    }
    // 引入monaco-editor
    const importMonaco = () => {
      if (monacoInited === true) {
        return Promise.resolve()
      }
      return importScript('https://g.alicdn.com/code/lib/monaco-editor/0.34.1/min/vs/loader.js')
        .then(() => {
          window.require.config({ paths: { vs: '//g.alicdn.com/code/lib/monaco-editor/0.34.1/min/vs' } })
          monacoInited = true
        })
        .catch(() => {
          monacoInited = false
        })
    }
    // 添加编辑器worker
    ;(self as any).MonacoEnvironment = {
      getWorkerUrl: function (workerId, label) {
        return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
            self.MonacoEnvironment = {
              baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.1/min/'
            };
            importScripts('https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.1/min/vs/base/worker/workerMain.js');`)}`
      },
    }
    // 初始化编辑器
    const initMonaco = () => {
      importMonaco().then(() => {
        window.require(['vs/editor/editor.main'], () => {
          if (codeEditBox.value) {
            editor = window.monaco.editor.create(codeEditBox.value, {
              value: props.modelValue,
              language: props.language,
              theme: props.theme,
              ...props.options,
            })
            window.monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
              noSemanticValidation: false,
              noSyntaxValidation: false,
            })
            window.monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
              target: window.monaco.languages.typescript.ScriptTarget.ES2020,
              allowNonTsExtensions: true,
            })
            // 监听值的变化
            editor.onDidChangeModelContent(() => {
              const value = editor.getValue() // 给父组件实时返回最新文本
              emit('update:modelValue', value)
              emit('change', value)
            })
            emit('editor-mounted', editor)
          }
        })
      })
    }
    onBeforeUnmount(() => {
      editor.dispose() // 销毁编辑器
    })

    onMounted(() => {
      initMonaco()
    })

    watch(
      () => props.modelValue,
      newValue => {
        if (editor) {
          const value = editor.getValue()
          if (newValue !== value) {
            editor.setValue(newValue)
          }
        }
      }
    )

    watch(
      () => props.options,
      newValue => {
        editor.updateOptions(newValue)
      },
      { deep: true }
    )

    watch(
      () => props.readOnly,
      () => {
        editor.updateOptions({ readOnly: props.readOnly })
      },
      { deep: true }
    )

    watch(
      () => props.language,
      newValue => {
        window.monaco.editor.setModelLanguage(editor.getModel()!, newValue)
      }
    )

    return {
      codeEditBox,
    }
  },
})
</script>

<style scoped lang="scss">
.code-edit-container {
  width: 100%;
  flex: 1;
  height: 100%;
  overflow-y: auto;
}
</style>
monacoEditorType文件
import { PropType } from 'vue'

export type Theme = 'vs' | 'hc-black' | 'vs-dark'
export type FoldingStrategy = 'auto' | 'indentation'
export type RenderLineHighlight = 'all' | 'line' | 'none' | 'gutter' // 行亮
export interface Options {
  automaticLayout: boolean // 自适应布局
  foldingStrategy: FoldingStrategy // 折叠方式  auto | indentation
  renderLineHighlight: RenderLineHighlight // 行亮
  selectOnLineNumbers: boolean // 显示行号
  placeholder: string
  minimap: {
    // 关闭小地图
    enabled: boolean
  }
  // readOnly: Boolean // 只读
  fontSize: number // 字体大小
  scrollBeyondLastLine: boolean // 取消代码后面一大段空白
  overviewRulerBorder: boolean // 不要滚动条的边框
}

export const editorProps = {
  modelValue: {
    type: String as PropType<string>,
    default: null,
  },
  width: {
    type: [String, Number] as PropType<string | number>,
    default: '100%',
  },
  height: {
    type: [String, Number] as PropType<string | number>,
    default: '100%',
  },
  language: {
    type: String as PropType<string>,
    default: 'javascript',
  },
  readOnly: {
    type: Boolean,
    default: false,
  },
  theme: {
    type: String as PropType<Theme>,
    validator(value: string): boolean {
      return ['vs', 'hc-black', 'vs-dark', 'hc-light'].includes(value)
    },
    default: 'vs-dark',
  },
  options: {
    type: Object as PropType<Options>,
    default() {
      return {
        automaticLayout: true,
        foldingStrategy: 'indentation', // 折叠方式  auto | indentation
        renderLineHighlight: 'all', // 行亮
        selectOnLineNumbers: true, // 显示行号
        minimap: {
          // 关闭小地图
          enabled: true,
        },
        placeholder: 'please enter...',
        // readOnly: false, // 只读
        fontSize: 16, // 字体大小
        scrollBeyondLastLine: false, // 取消代码后面一大段空白
        overviewRulerBorder: false, // 不要滚动条的边框
      }
    },
  },
}

在父组件中使用

<template>
    <MonacoEditor v-model="value" :language="codeLanguage" width="100%" height="100%" @editor-mounted="editorMounted" />
</template>
 <script lang="ts" setup>
    import { ref } from 'vue'
    import MonacoEditor from '@/components/MonacoEditor/index.vue'
    const value = ref('let a = 234') // 编辑器代码显示的值
    const codeLanguage = ref('javascript') // 要加载的语言类型
    // 加载完成操作
    const editorMounted = (editor: any) => {
      console.log('editor实例加载完成', editor)
    }
  </script>

Logo

前往低代码交流专区

更多推荐