乐于分享
好东西不私藏

Vue 项目嵌入 VS Code 编辑器?Monaco Editor 就你了!

Vue 项目嵌入 VS Code 编辑器?Monaco Editor 就你了!

Monaco Editor 是 VS Code 背后的代码编辑器,功能极其强大:

  • • 语法高亮(100+ 语言)
  • • 智能代码补全(IntelliSense)
  • • 代码格式化
  • • 错误提示
  • • Diff 对比视图
  • • 多光标编辑

在线代码编辑器、低代码平台、SQL 编辑器、配置文件编辑……这些场景都可以用 Monaco Editor。

安装

pnpm add @guolao/vue-monaco-editor

基础用法

<script setup>  import { VueMonacoEditor } from "@guolao/vue-monaco-editor";  import { ref } from "vue";  const code = ref(`function hello() {  console.log('Hello, Monaco!')}`);  const language = ref("javascript");  const theme = ref("vs-dark");</script><template>  <VueMonacoEditor    v-model:value="code"    :language="language"    :theme="theme"    :height="400"    :options="{      fontSize: 14,      minimap: { enabled: false },      scrollBeyondLastLine: false,      automaticLayout: true,    }"  /></template>

完整配置

<script setup>  import { VueMonacoEditor } from '@guolao/vue-monaco-editor'  import { ref, shallowRef } from 'vue'  const editorRef = shallowRef()  const code = ref('')  // 编辑器挂载完成  function handleMount(editor) {    editorRef.value = editor    // 聚焦编辑器    editor.focus()  }  // 格式化代码  function formatCode() {    editorRef.value?.getAction('editor.action.formatDocument')?.run()  }  // 获取选中的代码  function getSelectedCode() {    const selection = editorRef.value?.getSelection()    const model = editorRef.value?.getModel()    return model?.getValueInRange(selection)  }  // 插入代码片段  function insertSnippet(snippet: string) {    editorRef.value?.trigger('keyboard', 'type', { text: snippet })  }</script><template>  <div class="editor-container">    <div class="editor-toolbar">      <select v-model="language">        <option value="javascript">JavaScript</option>        <option value="typescript">TypeScript</option>        <option value="python">Python</option>        <option value="sql">SQL</option>        <option value="json">JSON</option>        <option value="html">HTML</option>        <option value="css">CSS</option>      </select>      <select v-model="theme">        <option value="vs">Light</option>        <option value="vs-dark">Dark</option>        <option value="hc-black">High Contrast</option>      </select>      <button @click="formatCode">格式化</button>    </div>    <VueMonacoEditor      v-model:value="code"      :language="language"      :theme="theme"      :height="500"      :options="{        fontSize: 14,        tabSize: 2,        minimap: { enabled: true },        lineNumbers: 'on',        wordWrap: 'on',        automaticLayout: true,        scrollBeyondLastLine: false,        formatOnPaste: true,        formatOnType: true,      }"      @mount="handleMount"    />  </div></template>

Diff 代码对比

<script setup>  import { VueMonacoDiffEditor } from "@guolao/vue-monaco-editor";  const original = ref(`function add(a, b) {  return a + b}`);  const modified = ref(`function add(a: number, b: number): number {  return a + b}`);</script><template>  <VueMonacoDiffEditor    :original="original"    :modified="modified"    language="typescript"    theme="vs-dark"    :height="400"    :options="{      renderSideBySide: true, // 并排显示      readOnly: true,    }"  /></template>

自定义语言和主题

// 注册自定义语言import * as monaco from "monaco-editor";monaco.languages.register({ id: "myLang" });monaco.languages.setMonarchTokensProvider("myLang", {  tokenizer: {    root: [      [/\[error.*/, "custom-error"],      [/\[notice.*/, "custom-notice"],      [/\[info.*/, "custom-info"],      [/\[[a-zA-Z 0-9:]+\]/, "custom-date"],    ],  },});// 注册自定义主题monaco.editor.defineTheme("myTheme", {  base: "vs-dark",  inherit: true,  rules: [    { token: "custom-error", foreground: "ff0000", fontStyle: "bold" },    { token: "custom-notice", foreground: "FFA500" },    { token: "custom-info", foreground: "008000" },    { token: "custom-date", foreground: "008080" },  ],  colors: {    "editor.background": "#1a1a2e",  },});

包体积处理

Monaco Editor 很大(~5MB),需要用 CDN 或 Worker 方式加载:

// vite.config.tsimport { defineConfig } from "vite";import monacoEditorPlugin from "vite-plugin-monaco-editor";export default defineConfig({  plugins: [    monacoEditorPlugin({      languageWorkers: [        "editorWorkerService",        "typescript",        "json",        "html",        "css",      ],    }),  ],});

或者用 CDN 加载(不打包进 bundle):

// 使用 CDN 版本import { install as VueMonacoEditorPlugin } from "@guolao/vue-monaco-editor";app.use(VueMonacoEditorPlugin, {  paths: {    vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs",  },});

SQL 编辑器

<script setup>  import { VueMonacoEditor } from "@guolao/vue-monaco-editor";  import { shallowRef } from "vue";  const editorRef = shallowRef();  const sql = ref("SELECT * FROM users WHERE id = 1;");  // 添加 SQL 关键词提示  function handleMount(editor, monaco) {    monaco.languages.registerCompletionItemProvider("sql", {      provideCompletionItems: (model, position) => {        const suggestions = [          "SELECT",          "FROM",          "WHERE",          "JOIN",          "LEFT JOIN",          "GROUP BY",          "ORDER BY",          "LIMIT",          "INSERT INTO",          "UPDATE",          "DELETE FROM",          "CREATE TABLE",        ].map((keyword) => ({          label: keyword,          kind: monaco.languages.CompletionItemKind.Keyword,          insertText: keyword,        }));        return { suggestions };      },    });  }  async function executeSQL() {    const result = await $fetch("/api/sql/execute", {      method: "POST",      body: { sql: sql.value },    });    console.log(result);  }</script><template>  <div>    <VueMonacoEditor      v-model:value="sql"      language="sql"      theme="vs-dark"      :height="200"      @mount="handleMount"    />    <button @click="executeSQL">执行 SQL</button>  </div></template>

写在最后

Monaco Editor 功能强大,但包体积大,要注意优化:

  • • 用 CDN 加载,不打包进 bundle
  • • 只注册需要的语言 Worker
  • • 懒加载编辑器组件(defineAsyncComponent

文档:

  • • Monaco Editor: https://microsoft.github.io/monaco-editor/
  • • @guolao/vue-monaco-editor: https://github.com/imguolao/monaco-vue

#MonacoEditor #Vue代码编辑器 #在线IDE #语法高亮 #Diff编辑器 #Vue3