Monaco-Editor 助力 AI 开发平台:浏览器直接变身“类 IDE”!

在当今飞速发展的 AI 领域,不少独立开发者、Java/Python 后端同学,乃至前端工程师,都希望能随时随地地快速查看并调试 AI 生成的代码。然而,一直依赖本地的 IDE(如 IntelliJ、VS Code 等)显得略微“笨重”,且在某些受限环境下可能并不方便。有没有一种既能保留我们常用 IDE 体验、又能轻量化地嵌入到浏览器的解决方案呢?Monaco-Editor 就是这样一把利器!

一、开发者“小A”的故事

为了让文章更加亲和,我们来讲个故事:开发者“小A”最近在做一个 AI 开发平台,希望能通过网页端直接展示、编辑 AI 生成的代码。但是,“小A”发现浏览器里要想实现 IDE 级别的自动高亮、自动完成、格式化等功能,并不容易。对比下来,直接将 VS Code 编辑器“搬”进网页也许是个不错的办法。于是,“小A”找到了 Monaco-Editor —— 这是 VS Code 的底层编辑器,能在浏览器端轻松复刻很多 VS Code 的体验。

下面,我们就从零开始,Step by Step 地展示如何在基于 Node 的项目中安装并使用 Monaco-Editor,并以 Vue3 + Vite 为例,介绍其核心配置与集成思路,最终让你在浏览器中享受到几乎“类 IDE”体验。

二、准备工作:安装与基本原理

1. 安装 Monaco-Editor

在任何基于 Node.js 的前端项目中,都可以通过下列方式安装 Monaco-Editor:

npm install monaco-editor

或者

yarn add monaco-editor

随后,你就可以在业务代码中直接 import 来使用它。Monaco-Editor 是开源的,并且提供了丰富的 API 能力,能让你在浏览器中实现自动补全、语法高亮、格式化、错误提示等丰富功能。

2. 为什么还需要配置 Worker?

VS Code 之所以那么强大,离不开各种各样的语言服务,如 TypeScript、JSON、CSS、HTML、Editor 等,这些服务实际上是运行在不同的 Web Worker 线程中,以保证主线程不会被语言解析、校验所阻塞。Monaco-Editor 也采用了同样的设计,因此需要指定相应的 Worker 来进行语言解析、语法提示、校验等工作。

你在代码中会看到类似下面的配置逻辑(这是以 Vite 的 ?worker 语法为例,动态引入各种 Worker):

import * as monaco from 'monaco-editor';

// 使用 Vite 的 ?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';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';

// 配置 MonacoEnvironment 以使用正确的 Worker 实例
self.MonacoEnvironment = {
    getWorker: function (workerId, label) {
        switch (label) {
            case 'json':
                return new JsonWorker();
            case 'css':
            case 'scss':
            case 'less':
                return new CssWorker();
            case 'html':
            case 'handlebars':
            case 'razor':
                return new HtmlWorker();
            case 'typescript':
            case 'javascript':
                return new TsWorker();
            default:
                return new EditorWorker();
        }
    }
};

export default monaco;

这里的 self.MonacoEnvironment 便是 Monaco-Editor 运行时需要的环境配置。当编辑器创建语言服务时,会根据“标签”(label,即 jsoncsshtmltypescriptjavascript 等)来确定要使用哪一种 Worker,从而完成相应的语法高亮、自动完成、错误检查等功能。没有这一步,你可能就会遇到自动完成功能失效、报错的信息等问题

三、Vue3 + Vite 使用示例

在了解了配置 Worker 的基本原理后,“小A”也想在自己的 Vue3 + Vite 项目里集成 Monaco-Editor。下面的示例代码演示了如何进行编辑器的初始化、语言/主题动态切换,以及如何添加自定义功能(比如通过一个右键菜单按钮触发简单的“AI 优化”对话框)。

扫描二维码关注公众号,回复: 17576660 查看本文章

友情提示:以下示例中使用了部分 Vue3 的语法(如 <script setup>refwatchdefinePropsdefineEmits 等)。如果你是 React/Angular 开发者,也可以类比处理,Monaco-Editor 的调用方式在不同框架间比较类似。

1. 编写 monaco 工具文件

很多时候,我们会单独抽出一个文件(例如 @/utils/monaco)用来配置并导出 monaco 对象。其中包含了上面提到的 Worker 配置,如下所示。

// @/utils/monaco.js
import * as monaco from 'monaco-editor';

// 使用 Vite 的 ?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';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';

// 配置 MonacoEnvironment 以使用正确的 Worker 实例
self.MonacoEnvironment = {
    getWorker: function (workerId, label) {
        switch (label) {
            case 'json':
                return new JsonWorker();
            case 'css':
            case 'scss':
            case 'less':
                return new CssWorker();
            case 'html':
            case 'handlebars':
            case 'razor':
                return new HtmlWorker();
            case 'typescript':
            case 'javascript':
                return new TsWorker();
            default:
                return new EditorWorker();
        }
    }
};

export default monaco;

2. 在 Vue 组件中创建编辑器

下面是一个示例组件 MonacoEditor.vue,展示了如何在 Vue3 环境里初始化 Monaco 编辑器、监听外部属性变化并同步到编辑器、以及在编辑器内部对内容修改进行监听。还添加了一个自定义的右键菜单按钮“AI优化”,打开一个模态窗口输入“修改意见”。

<template>
    <div class="editor-container" ref="editorContainer"></div>

    <a-modal v-model:open="modalOpen" title="AI优化" @ok="handleOk" centered>
        <a-textarea v-model:value="modifyContent" :rows="4" placeholder="请输入修改意见" />
    </a-modal>
</template>

<script setup>
import monaco from "@/utils/monaco";
import { onMounted, onUnmounted, ref, watch, defineEmits } from "vue";
import { useStoreByName } from '@/views/system/function/editor/base/UseStore';

// Emit setup
const emit = defineEmits(["update:modelValue", "modifyContent"]);

// Props
const props = defineProps({
    modelValue: {
        type: String,
        default: "console.log('Hello, Monaco!');",
    },
    language: {
        type: String,
        default: "javascript",
    },
    description: {
        type: String,
        default: "",
    },
    theme: {
        type: String,
        default: "vs-dark",
    },
    formatImmediate: {
        type: Boolean,
        default: false,
    },
    customAction: {
        type: Boolean,
        default: true,
    },
});

// Refs
const editorContainer = ref(null);
let editor = null;
const modalOpen = ref(false);
const modifyContent = ref('');

// 监听语言变化,动态切换 Monaco 编辑器的语言并自动格式化
watch(
    () => props.language,
    (newValue) => {
        if (editor && editor.getModel()) {
            const model = editor.getModel();
            // 动态设置语言
            monaco.editor.setModelLanguage(model, newValue);
            // 自动格式化
            editor.getAction("editor.action.formatDocument").run();
        }
    }
);

// 监听外部传入的代码内容,若与内部不一致则更新
watch(
    () => props.modelValue,
    (newValue) => {
        if (!editor) return;
        const currentEditorValue = editor.getValue();
        if (newValue !== currentEditorValue) {
            editor.setValue(newValue);
            if (props.formatImmediate) {
                editor.getAction("editor.action.formatDocument").run();
            }
        }
    }
);

// 组件挂载后,创建 Monaco 编辑器实例
onMounted(() => {
    editor = monaco.editor.create(editorContainer.value, {
        value: props.modelValue,
        language: props.language,
        theme: props.theme,
        automaticLayout: true,
    });

    // 监听编辑器内容变化,向父组件同步最新代码
    editor.onDidChangeModelContent(() => {
        const newValue = editor.getValue();
        emit("update:modelValue", newValue);
    });

    // 添加自定义右键菜单操作(AI优化)
    if (props.customAction) {
        editor.addAction({
            id: "action-ai-rewrite",
            label: "AI优化",
            contextMenuGroupId: "navigation",
            contextMenuOrder: 1.5,
            run(ed) {
                modalOpen.value = true;
            },
        });
    }

    // 自动格式化
    editor.getAction("editor.action.formatDocument").run();
});

// 组件卸载时,释放编辑器资源
onUnmounted(() => {
    if (editor) {
        editor.dispose();
    }
});

// 假设还有一个 store,用于保存我们的 AI 请求信息
const llmMessageStore = useStoreByName('llmMessage', 'newMessage');

// 当点击“AI优化”对话框的OK按钮,进行一些处理
function handleOk() {
    modalOpen.value = false;
    llmMessageStore.value = `当前内容描述:${props.description},请对当前内容进行优化:${editor.getValue()},具体优化意见为:1.${modifyContent.value}`;
}
</script>

<style scoped>
.editor-container {
    width: 100%;
    height: 99%;
    border: 1px solid #ccc;
    margin-top: 5px;
    margin-bottom: 5px;
}
</style>
这里做了哪些事?
  1. editorContainer:通过 ref 拿到一个 DOM 容器,作为 Monaco-Editor 的挂载点。
  2. 自动设置语言:通过监听 props.language 改变,动态调用 monaco.editor.setModelLanguage 切换语言模式。
  3. 自动格式化:使用 editor.getAction("editor.action.formatDocument").run(),即可让 Monaco 帮你整理代码排版。
  4. 内容同步:通过 watch 监听父组件的 modelValue,并在编辑器内部发生变动时向父组件 emit("update:modelValue", newValue)
  5. 自定义右键菜单:注册 editor.addAction,将“AI优化”按钮插入到编辑器的右键菜单。

3. 父组件如何使用 MonacoEditor

如果你要把这个编辑器真正用起来,可以在顶层或任意父组件里,通过如下方式集成:

<template>
    <MonacoEditor
        :description="description"
        v-model="editorContent"
        language="json"
        theme="vs"
        :format-immediate="true"
    />
</template>

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

const editorContent = ref('{ "key": "value" }');
const description = ref('这是一个 JSON 示例配置');
</script>

在这里:

  • v-model="editorContent" 绑定了编辑器的内容,随着编辑器内改动,editorContent 会实时更新。
  • language="json" 切换编辑器语言模式为 JSON。
  • theme="vs" 代表 MonacoEditor 的主题样式会走“vs”(浅色主题),而不是默认的“vs-dark”。
  • format-immediate 可以让编辑器在初次加载或内容更新之后,立即执行自动格式化。

四、更多价值与技术细节

1. 多语言支持

Monaco-Editor 能够支持多种语言(JavaScript、TypeScript、C/C++、Python、HTML、JSON、CSS 等),对于 AI 平台而言,这意味着无论 AI 生成的是什么语言,都可以轻松切换到相应的语法高亮和提示。

2. 自动完成与错误检查

若你使用 TypeScript 或 JavaScript 语言模式,Monaco-Editor 会默认附带非常强大的自动完成与错误检查,与 VS Code 中的表现几乎相同。这对于在浏览器端快速验证 AI 生成代码的合法性、或在与后端的交互逻辑里第一时间捕捉明显的语法错误,具有非常重要的意义。

3. 拓展性与可定制性

Monaco-Editor 还支持注册自定义命令、右键菜单扩展、代码段(snippets)等,让你可以针对不同 AI 平台的需求量身定制。比如在上方示例里的“AI优化”功能,就能够轻松将编辑器内的内容提交给一个后台服务或大语言模型,让 AI 来给你做进一步的解释、改写或纠错。

4. 学习成本低

使用 Monaco-Editor 并不需要记忆太多专有 API,如果你对 VS Code 的操作习惯已经轻车熟路,那么相同的快捷键、相同的配置理念在 Monaco-Editor 中也能找到对应点。并且它的入门文档对于熟悉 JavaScript/HTML 的开发者比较友好,几行代码就能“让网页变身 IDE”

五、总结

Monaco-Editor 的引入,无疑为 AI 开发平台的前端架构提供了一种轻量化却功能强大的方案。它像一把“瑞士军刀”,帮助开发者在浏览器端就能获得类似于 VS Code 的编辑体验——高亮、自动完成、格式化、实时校验、右键扩展……统统不在话下。对于独立开发者,或正在做 Java/Python/前端项目的团队来说,Monaco-Editor 几乎 是一个“零替代品”的存在。

当你要把 AI 生成的各种脚本交付给用户浏览、编辑、调试时,不再需要要求对方重度依赖本地 IDE:只需打开浏览器,就能立即获得在线“类 IDE”的体验,大幅降低了“环境配置”成本,同时也让协作分享更加灵活高效。

最后,如果你心动了,赶紧动手在项目中试试吧!别忘了多挖掘 Monaco-Editor 的功能,比如 Keybinding、Hover Tooltip、自定义语言等,相信在 AI 时代,它能为你的项目增添不一样的“科技感”与“生产力”!祝你开发愉快、码字顺利!