vue3+vite+monaco 实现json编辑

1,449 阅读2分钟

Monaco与VSCode

微软之前有个项目叫做Monaco Workbench,后来这个项目变成了VSCode,而Monaco Editor(下文简称monaco)就是从这个项目中成长出来的一个web编辑器,他们很大一部分的代码(monaco-editor-core)都是共用的,所以monaco和VSCode在编辑代码,交互以及UI上几乎是一摸一样的,有点不同的是,两者的平台不一样,monaco基于浏览器,而VSCode基于electron,所以功能上VSCode更加健全,并且性能比较强大。

Monaco 是一个用于浏览器中的编辑器,始于 2011 年 Erich Gamma 加入微软。后期主要使用在 Visual Studio Online, OneDrive 以及不少内部网站。最终使用 Electron 包装成为一个跨平台的编辑器,当然其实她还是在一个浏览器里。

官方demo示例:microsoft.github.io/monaco-edit…

如何使用:

这里只讲述使用monaco实现json编辑,如果想要实现代码编辑,可以参考该项目github.com/Tencent/tma…

  1. 安装 npm install *monaco*-*editor* --save

image.png

  1. main.ts 中提供一个定义worker路径的全局变量
import { createApp } from 'vue'

import * as monaco from 'monaco-editor';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.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 JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';

import './style.css'
import App from './App.vue'

// @ts-ignore
// vite/webpack搭建参考 https://github.com/microsoft/monaco-editor/blob/main/docs/integrate-esm.md
globalThis.MonacoEnvironment = {
    getWorker(_: any, label: string) {
        if (label === 'json') {
            return new JsonWorker();
        }
        if (label === 'css' || label === 'scss' || label === 'less') {
            return new CssWorker();
        }
        if (label === 'html' || label === 'handlebars' || label === 'razor') {
            return new HtmlWorker();
        }
        if (label === 'typescript' || label === 'javascript') {
            return new TsWorker();
        }

        return new EditorWorker();
    },
};

monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);

createApp(App).mount('#app')
  1. vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [vue()],
    
    optimizeDeps: {
        esbuildOptions: {
            define: {
                global: 'globalThis',
            },
        },
    },
})

  1. 组件中使用
<template>
    <div ref="jsonEditorRef" class="monaco-editor-content"></div>
</template>

<script lang="ts" setup>
import { onMounted, onUnmounted, ref } from 'vue'
import { throttle } from 'lodash-es'
import * as monaco from 'monaco-editor'

const jsonEditorRef = ref<HTMLDivElement>()

let jsonEditor: monaco.editor.IStandaloneCodeEditor | null = null

// 实现 monaco 自适应
const resizeObserver = new globalThis.ResizeObserver(
    throttle((): void => {
        jsonEditor?.layout()
    }, 300)
)

const setEditorValue = (v: string | any) => {
    const jsonValue = JSON.parse(JSON.stringify(v))

    // json格式化
    return JSON.stringify(jsonValue, null, 2)
}

const defaultValue = {
    name: 'lucy',
    age: 18,
}

const init = async () => {
    if (!jsonEditorRef.value) return

    let options = {
        value: setEditorValue(defaultValue),
        language: 'json',
        theme: 'vs-dark',
        tabSize: 2, // tab 缩进长度
        minimap: {
            enabled: false, // 不要小地图
        },
    }

    jsonEditor = monaco.editor.create(jsonEditorRef.value, options)

    resizeObserver.observe(jsonEditorRef.value)
}

onMounted(async () => {
    init()
})

onUnmounted(() => {
    resizeObserver.disconnect()
})
</script>
<style lang="less">
.monaco-editor-content {
    width: 100vw;
    height: 70vh;
}
</style>

  1. 搭建出的json编辑器

image.png 带有错误提示,并且由于我们直接对传入的数据进行了格式上的调整,所以展示出来不是一行

Tips:有一些细节方面需要注意:

  1. 由于使用vite,所以node版本至少为16
  2. jsonEditor?.trigger('', 'editor.action.formatDocument', '');可以通过这种方式来实现对json数据的格式调整,但在实操中,通过jsonEditor?.setValue(JSON.parse(v))对json数据更新后,执行该操作,首次进入时,json仍旧以一行排列,第二次进入,才会格式调整,所以通过JSON.stringify(jsonValue, null, 2),在setValue之前,直接对json进行格式调整,然后塞入monaco
  3. 为什么注册全局变量worker路径&self变量?juejin.cn/post/684490…