夜猫的小站

monaco-editor使用amd方式在vue中使用

Published on
阅读时间:5分钟963

本文最近一次更新于 1436 个天前,其中的内容很可能已经有所发展或是发生改变。

前言

monaco-editor是微软vscode的核心编辑器,除了在客户端中使用外,官方也支持在web端使用。由于我的博客想要一个能像vscode一样编辑页面的编辑器,所以采用了monaco-editor实现了一个页面编辑器。本文记录了monaco-editor在vue项目中的使用记录。

初探

原本是想直接引用vue封装好的库vue-monaco-editor,单后来发现不管是vue的库还是react的库react-monaco-editor都是基于monaco的esm的方式引入,这就导致了这些库只支持英文的菜单栏展示,不支持多语言。 想要向vscode一样支持多语言,可以以AMD方式引入monaco-editor,在esm上没找到合适快捷的方式,尝试多次后,我最终还是使用AMD方式引入。

2023.10.10 更新 在 react 的库里, https://github.com/suren-atoyan/monaco-react 作者用 typescript 重写的微软的 monaco-editor,可以正常使用 esm 方式引入了

monaco-editor的AMD方式引入

官方示例:browser-amd-localized

<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 </head>
 <body>
  <h2>Monaco Editor Localization Sample</h2>
  <div id="container" style="width: 800px; height: 600px; border: 1px solid grey"></div>

  <script src="../node_modules/monaco-editor/min/vs/loader.js"></script>
  <script>
   require.config({ paths: { vs: '../node_modules/monaco-editor/min/vs' } });

   require.config({
    'vs/nls': {
     availableLanguages: {
      '*': 'zh-cn'
     }
    }
   });

   require(['vs/editor/editor.main'], function () {
    var editor = monaco.editor.create(document.getElementById('container'), {
     value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
     language: 'javascript'
    });
   });
  </script>
 </body>
</html>

通过cdn引入

<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdn.jsdelivr.net/npm/monaco-editor@0.24.0/min/vs/editor/editor.main.css" />
 </head>
 <body>
  <h2>Monaco Editor Localization Sample</h2>
  <div id="container" style="width: 800px; height: 600px; border: 1px solid grey"></div>
  <script>
   self.require = {
    paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.24.0/min/vs' },
    'vs/nls': { availableLanguages: { '*': 'zh-cn' } }
   };
  </script>
  <script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.24.0/min/vs/loader.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.24.0/min/vs/editor/editor.main.nls.zh-cn.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.24.0/min/vs/editor/editor.main.js"></script>
  <script>
   monaco.editor.create(document.getElementById('container'), {
    value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
    language: 'javascript'
   });
  </script>
 </body>
</html>

国际化是以下代码实现的

self.require = {
    paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.24.0/min/vs' },
    'vs/nls': { availableLanguages: { '*': 'zh-cn' } }
};

paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.24.0/min/vs' }这个对cdn引入的方式很重要的,如果没有就会导致跨域问题。

在vue中使用

amd方式引入后,其实是将monaco挂载到window对象上,在vue上直接使用window.monaco就可以了。

1.封装成组件

结合vue-monaco-editor的源码,封装一下编辑器,代码如下:

<!-- monacoEditor -->
<template>
  <div class="monaco-editor">
  </div>
</template>

<script>
/**
 * monacoEditor
 * @description:
 * @file: index.vue
 * @author: example <example@raycloud.com>
 * @date: 2021-05-21
 */
import _ from 'lodash'
export default {
    name: 'MonacoEditor',
    props: {
      code: { type: String, default: '// code \n' },
      language: { type: String, default: 'javascript' },
      theme: { type: String, default: 'vs-dark' }, // vs, hc-black
      options: {
        type: Object, default: () => {}
      },
      highlighted: {
        type: Array,
        default: () => [{
            number: 0,
            class: ''
        }]
      },
      // 更新频率
      changeThrottle: {
        type: Number, default: 0
      }
    },
    components: {},
    data () {
        return {
          defaults: {
            selectOnLineNumbers: true,
            roundedSelection: false,
            readOnly: false,
            cursorStyle: 'line', // 光标样式
            automaticLayout: true, // 自动布局
            glyphMargin: true, // 字形边缘
            lineNumbersMinChars: 0,
            fontSize: 12, // 字体大小
            autoIndent: false,

            wordWrap: 'wordWrapColumn',
            wordWrapColumn: 100
          }
        }
    },
    computed: {
      editorOptions () {
        return Object.assign({}, this.defaults, this.options, {
          value: this.code,
          language: this.language,
          theme: this.theme
        })
      }
    },
    watch: {
      language () {
        window.monaco.editor.setModelLanguage(this.editor.getModel(), this.language)
      }
    },
    methods: {
      createEditor () {
        this.editor = window?.monaco?.editor.create(this.$el, this.editorOptions)
        this.initEditor(this.editor, window.monaco)
      },
      initEditor (editor, monaco) {
        this.editor = editor
        this.monaco = monaco
        this.editor.onDidChangeModelContent(event => {
          this.codeChange(editor, event)
        })
        this.$emit('mounted', editor)
      },
      codeChange (editor, ev) {
        if (this.codeChangeEmitter) {
          this.codeChangeEmitter(editor)
        } else {
          this.codeChangeEmitter = _.debounce(function (editor) {
            this.$emit('codeChange', editor)
          }, this.changeThrottle)
        }
      }
    },
    created () {

    },
    mounted () {
      this.createEditor()
    },
    destroyed () {
      if (typeof this.editor !== 'undefined') {
        this.editor.dispose()
      }
    }
}
</script>

2.在项目中使用

<template>
    <Editor
        :code="code"
        :language="language"
        @codeChange="handleChange"
        @mounted="onMounted"
        :editorOptions="editorOptions"
        style="width: 700px; height: 480px; border: 1px solid grey"
    />
</template>
<script>
import Editor from './xxx/MonacoEditor'
export default {
    components: {
      Editor
    },
    data () {
      return {
        editor: null,
          code: '// 默认展示',
          language: 'html',
          editorOptions: {
          },
        }
    },
    methods: {
      onMounted (editor) {
        this.editor = editor
      },
      handleChange (editor) {
        this.code = editor.getValue()
      }
    },
}
</script>

3.设置主题、设置编辑语言高亮支持

例如设置背景,设置主题之类的自定义的实现方式可以参考官方的例子 https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-semantic-tokens-provider-example

参考文章