
使用 Monaco 编辑器构建 Vue 3 代码编辑器组件
Monaco 编辑器是一个功能强大的代码编辑器,它广泛应用于 Visual Studio Code 中。通过其提供的 API,可以在 Web 项目中集成强大的代码编辑功能。本文将介绍如何在 Vue 3 中构建一个 Monaco 编辑器组件,并提供相关的使用示例、依赖和源码。
1. 项目依赖
在使用 Monaco 编辑器之前,你需要安装 monaco-editor
包:
npm install monaco-editor
2. 组件源码
我们将构建一个 Monaco 编辑器的 Vue 3 组件,它支持动态设置编辑器的代码内容和语言,并暴露接口供父组件获取或修改编辑器中的代码。
2.1 组件模板
<template>
<div class="codemirror">
<div id="monacoEditor" class="monaco-editor" ref="monacoEditor"></div>
</div>
</template>
这个模板定义了一个 div
元素作为 Monaco 编辑器的容器,并使用 ref
来引用它,以便在 JavaScript 中访问和操作。
2.2 组件脚本
<template>
<div class="codemirror">
<div id="monacoEditor" class="monaco-editor" ref="monacoEditor"></div>
</div>
</template>
<script setup>
import { ref, watch, onMounted, onBeforeUnmount , defineProps, toRaw} from "vue";
import * as monaco from "monaco-editor";
// 动态引入 Monaco 编辑器的 Worker 文件
const loadMonacoWorker = (workerId, label) => {
if (label === "json")
return import("monaco-editor/esm/vs/language/json/json.worker?worker");
if (label === "css" || label === "scss" || label === "less")
return import("monaco-editor/esm/vs/language/css/css.worker?worker");
if (label === "html")
return import("monaco-editor/esm/vs/language/html/html.worker?worker");
if (["typescript", "javascript"].includes(label))
return import("monaco-editor/esm/vs/language/typescript/ts.worker?worker");
return import("monaco-editor/esm/vs/editor/editor.worker?worker");
};
// 配置 Monaco 的 Worker 环境
self.MonacoEnvironment = {
getWorker(workerId, label) {
return loadMonacoWorker(workerId, label).then(
(workerModule) => new workerModule.default()
);
},
};
// 定义从父组件接收的属性
const props = defineProps({
option: {
type: Object,
default: () => ({
options: {
language: "javascript", // 默认语言
code: "// Write your code here", // 默认代码
},
}),
},
});
// 定义响应式变量
const monacoEditor = ref(null); // 编辑器容器
const editor = ref(null); // 编辑器实例
// 初始化编辑器
function initEditor(language, code) {
if (!monacoEditor.value) return;
editor.value = monaco.editor.create(monacoEditor.value, {
value: code,
theme: "vs-dark", // 主题
language: language,
automaticLayout: true,
folding: true,
scrollBeyondLastLine: false,
colorDecorators: true, // 颜色装饰器
accessibilitySupport: "on", // 辅助功能支持 "auto" | "off" | "on"
colors: {
"editor.background": "#131722", // 修改编辑器背景颜色
"editor.lineHighlightBackground": "#131722", // 当前行背景高亮颜色
"editorCursor.foreground": "#131722", // 光标颜色
},
});
// 添加自定义补全项
registerCustomCompletionProvider(language);
}
// 注册自定义补全提供者
function registerCustomCompletionProvider(language) {
monaco.languages.registerCompletionItemProvider(language, {
provideCompletionItems: (model, position) => {
const suggestions = [
{
label: "console.log", // 提示标签
kind: monaco.languages.CompletionItemKind.Function, // 类型
insertText: "console.log(${1:message});", // 插入的内容,支持占位符
insertTextRules:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, // 插入规则
documentation: "输出到控制台", // 文档描述
},
{
label: "for loop",
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: "for (let i = 0; i < ${1:array}.length; i++) {\n\t$0\n}",
insertTextRules:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: "简单的 for 循环代码模板",
},
];
return { suggestions };
},
});
}
// 监听 props.option 的变化
watch(
() => props.option,
(newOption) => {
if (editor.value) {
// 只更新编辑器内容和语言,而不是每次都重新初始化
if (newOption.options.code !== editor.value.getValue()) {
editor.value.setValue(newOption.options.code);
}
if (
newOption.options.language !== editor.value.getModel().getLanguageId()
) {
monaco.editor.setModelLanguage(
editor.value.getModel(),
newOption.options.language
);
}
} else {
// 仅在第一次初始化时创建编辑器
initEditor(newOption.options.language, newOption.options.code);
}
},
{ immediate: true, deep: false } // 禁用 deep 监听,避免每个嵌套的变化都触发
);
// 挂载时初始化编辑器
onMounted(() => {
initEditor(props.option.options.language, props.option.options.code);
});
// 销毁前清理编辑器实例
onBeforeUnmount(() => {
if (editor.value) {
editor.value.dispose();
}
});
// 在父组件中调用这个方法来获取代码内容
const getCode = () => {
// 确保编辑器已经初始化
if (editor.value) {
try {
// 使用 Monaco 编辑器实例的 getValue() 方法获取代码内容
let demo = toRaw(editor.value).getValue(); //获取编辑器中的文本
return demo
} catch (error) {
console.error('获取代码内容失败:', error);
return '';
}
}
return ''; // 返回空字符串,如果 editor 没有初始化
};
const setCode = (code) =>{
// 确保编辑器已经初始化
if (editor.value) {
try {
// 使用 Monaco 编辑器实例的 getValue() 方法获取代码内容
toRaw(editor.value).setModel(monaco.editor.createModel(code, props.option.options.language))
} catch (error) {
console.error('获取代码内容失败:', error);
return '';
}
}
return ''; // 返回空字符串,如果 editor 没有初始化
}
// 通过暴露 `getCode` 方法供父组件调用
defineExpose({
getCode,setCode
});
</script>
<style scoped>
.codemirror,
.monaco-editor {
width: 100%;
height: 100%;
background-color: var(--background-color);
}
</style>
2.3 组件样式
<style scoped>
.codemirror,
.monaco-editor {
width: 100%;
height: 100%;
background-color: var(--background-color);
}
</style>
3. 使用方式
该组件的使用方式非常简单,你只需要将它作为子组件引入,并通过 option
属性动态传递语言和代码内容。
3.1 父组件使用示例
<template>
<div>
<MonacoEditor :option="editorOptions" ref="codeEditor"/>
<button @click="handleGetCode">获取代码</button>
<button @click="handleSetCode">设置新代码</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import MonacoEditor from './MonacoEditor.vue'; // 引入 Monaco 编辑器组件
const editorOptions = ref({
options: {
language: 'javascript',
code: '// Type your JavaScript code here',
},
});
const codeEditor = ref(null);
// 获取编辑器中的代码内容
const handleGetCode = () => {
const code = codeEditor.value.getCode();
console.log('编辑器中的代码:', code);
};
// 设置编辑器中的代码内容
const handleSetCode = () => {
codeEditor.value.setCode('console.log("Hello World!");');
};
</script>
4. 关键功能说明
动态设置语言和代码:通过
props.option
,你可以传递不同的语言和初始代码到 Monaco 编辑器。编辑器会根据这些参数动态加载语言模式并展示相应的代码内容。获取和设置代码:通过暴露的
getCode
和setCode
方法,父组件可以获取或设置 Monaco 编辑器中的代码内容。这使得编辑器的使用更加灵活。自定义代码补全:我们注册了
console.log
和for loop
作为自定义的代码补全项。你可以根据需要,添加更多的补全功能。自动布局和主题支持:编辑器支持自动布局和自定义的主题。你可以根据需要更改编辑器的外观和行为。
5. 总结
通过上述方式,你可以在 Vue 3 项目中轻松集成 Monaco 编辑器,
构建功能强大的代码编辑器。组件提供了灵活的 API,可以方便地获取和设置代码内容,并支持语言切换和自定义补全等功能。
本文是原创文章,完整转载请注明来自 俞泊
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果