An overview of the

MonacoEdit is an online editor library provided by Microsoft, and vscode is based on this implementation, which now integrates MonacoEdit into the vue3 project

The solution is vite + VUe3

Implementation method

Start by initializing a VUe3 project

npm init @vitejs/app editor-proj --template vue
Copy the code

Create a project with the following directory structure

Editor - proj ├ ─ public │ └ ─ the favicon. Ico ├ ─ SRC │ ├ ─ assets │ │ └ ─ logo. The PNG │ ├ ─ components │ │ └ ─ the HelloWorld. Vue │ ├ ─ ├─ download.txt ├─ download.txt ├─ download.txtCopy the code

Next, install the Monaco-Editor dependency

yarn add monaco-editor
Copy the code

Implement a JSON editor component and reference it in app.vue, with the final directory structure as follows

Editor - proj ├ ─ public │ └ ─ the favicon. Ico ├ ─ SRC │ ├ ─ assets │ │ └ ─ logo. The PNG │ ├ ─ components │ │ └ ─ JsonEditor. Vue │ ├ ─ App.vue │ ├─ ├─ download.txt ├─ download.txt ├─ download.txt ├─ download.txtCopy the code

The implementation of jsonEditor. vue is as follows

<template>
  <div class="editor" ref="dom"></div>
</template>

<script setup>
import { onMounted, defineProps, defineEmit, ref } from 'vue';
import * as monaco from 'monaco-editor';

const props = defineProps({
  modelValue: String,
});

const emit = defineEmit(['update:modelValue']);

const dom = ref();

let instance;

onMounted(() => {
  const jsonModel = monaco.editor.createModel(
    props.modelValue,
    'json',
    monaco.Uri.parse('json://grid/settings.json')
  );

  instance = monaco.editor.create(dom.value, {
    model: jsonModel,
    tabSize: 2,
    automaticLayout: true,
    scrollBeyondLastLine: false,
  });

  instance.onDidChangeModelContent(() => {
    const value = instance.getValue();
    emit('update:modelValue', value);
  });
});
</script>

<style scoped>
.editor {
  height: 100%;
}
</style>
Copy the code

App.vue is referenced as follows

<template>
  <div class="container">
    <JsonEditor v-model="code"></JsonEditor>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import JsonEditor from './components/JsonEditor.vue';

const code = ref('');
</script>

<style scoped>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}

.container {
  position: fixed;
  height: 100%;
  width: 100%;
}
</style>
Copy the code

Run the yarn Run Dev project to see the editor effect

However, there were some problems. The input JSON data could not be formatted, and the browser reported an error

Error: Unexpected usage
    at EditorSimpleWorker.loadForeignModule (editorSimpleWorker.js:454)
    at webWorker.js:38
Copy the code

This is because the monaco-Editor implementation is based on the Web Worker, and the editor’s processing of the grammar is improved by enabling the asynchronous processing of the Worker. At this time, no Worker for processing the grammar has been loaded

Problems and solutions can be found at github.com/vitejs/vite…

The Monaco-Editor will fetch the MonacoEnvironment object in the global variable and execute getWorker getWorkerUrl to load the corresponding syntax processing

Json data syntax processing, modify jsoneditor. vue file, add the following implementation

import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker? worker';
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker? worker';

self.MonacoEnvironment = {
  getWorker(workerId, label) {
    if (label === 'json') {
      return new JsonWorker();
    }
    return newEditorWorker(); }};Copy the code

The final implementation of jsonEditor.vue is modified as follows

<template>
  <div class="editor" ref="dom"></div>
</template>

<script setup>
import { onMounted, defineProps, defineEmit, ref } from 'vue';
import * as monaco from 'monaco-editor';

import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';

self.MonacoEnvironment = {
  getWorker(workerId, label) {
    if (label === 'json') {
      return new JsonWorker();
    }
    return new EditorWorker();
  },
};

const props = defineProps({
  modelValue: String,
});

const emit = defineEmit(['update:modelValue']);

const dom = ref();

let instance;

onMounted(() => {
  const jsonModel = monaco.editor.createModel(props.modelValue, 'json');

  instance = monaco.editor.create(dom.value, {
    model: jsonModel,
    tabSize: 2,
    automaticLayout: true,
    scrollBeyondLastLine: false,
  });

  instance.onDidChangeModelContent(() => {
    const value = instance.getValue();
    emit('update:modelValue', value);
  });
});
</script>

<style scoped>
.editor {
  height: 100%;
}
</style>
Copy the code

When I run the project again, I find that THE JSON data is working properly, including the shortcut key formatting

If you want to support more syntax, just reference the corresponding Worker

import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker? worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker? worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker? worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker? worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker? worker';
Copy the code

Fill in the pit

The project was running at development time, but after the build was released, an exception was found

Uncaught ReferenceError: window is not defined
    at editor.worker.bfe8d272.js:1
Error: Unexpected usage
    at xm.loadForeignModule (editor.worker.bfe8d272.js:1)
    at editor.worker.bfe8d272.js:1
Copy the code

As mentioned before, the editor uses webworker and cannot obtain window and Document objects from Webworker. Analysis may be that the function-independent code is packaged together during packaging so that the modules loaded separately are put into Webworker and run away

Vite packaging is implemented based on rollup, where the manual sharding option of Rollup can be used to separately package worker-related items to solve this problem

The final implementation in vite. Config. js is as follows

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import jsx from '@vitejs/plugin-vue-jsx';

const prefix = `monaco-editor/esm/vs`;

export default defineConfig({
  base: '/'.build: {
    rollupOptions: {
      output: {
        manualChunks: {
          jsonWorker: [`${prefix}/language/json/json.worker`].cssWorker: [`${prefix}/language/css/css.worker`].htmlWorker: [`${prefix}/language/html/html.worker`].tsWorker: [`${prefix}/language/typescript/ts.worker`].editorWorker: [`${prefix}/editor/editor.worker`],},},},},plugins: [vue(), jsx()],
});
Copy the code