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
夜雨聆风