Monaco Editer 代码编辑器
Monaco Editer是微软开发 VScode同款编辑器,功能非常多,几乎可以覆盖页面级代码编辑器的所有使用场景,本文将基于Vite+Vue3封装一个简易的代码编辑器Demo。
- 仓库地址:https://github.com/CatiiHuang/VueCodeEditerDemo.git
- 支持双向绑定与config动态配置
- 支持[ ‘javascript’, ‘json’, ‘css’, ‘html’, ‘typescript’ ]代码自动补全,代码高亮
- 更多配置请看官网:https://microsoft.github.io/monaco-editor/
组件代码
<template>
<div ref="editerWrap" class="code-editer-wrap"></div>
</template>
<script setup>
// 依赖挂载
import monaco from './hooks/dependencies.js';
import { computed, onMounted, ref, watch } from 'vue';
let codeEditer = null;
const editerWrap = ref(null);
const props = defineProps({
modelValue: {
type: String,
default: '',
},
config: {
type: Object,
default: () => ({
value: ``,
fontSize: 14,
readOnly: false,
language: 'javascript',
}),
},
});
const emits = defineEmits(['change', 'update:modelValue', 'blur', 'focus']);
// 编辑器配置
const codeEditerConfig = computed(() =>
Object.assign(
{
value: props.modelValue,
fontSize: 14,
readOnly: false,
language: 'javascript',
automaticLayout: true,
minimap: {
enabled: true,
},
scrollBeyondLastLine: false,
overviewRulerBorder: false,
},
props.config
)
);
// 初始化编辑器
const initCodeEditer = () => {
codeEditer = monaco.editor.create(editerWrap.value, codeEditerConfig.value);
};
// 内容变化监听
const initCodeChange = () => {
codeEditer.onDidChangeModelContent(() => {
const value = getCodeEditerValue();
emits('update:modelValue', value);
emits('change', value);
});
};
// 焦点变化监听
const blurAndFoucsMoniter = () => {
codeEditer.onDidBlurEditorText((e) => {
emits('blur', e);
});
codeEditer.onDidFocusEditorText((e) => {
emits('focus', e);
});
};
// 获取内容
const getCodeEditerValue = () => codeEditer.getValue();
// 设置内容
const setCodeEditerValue = (val) => codeEditer.setValue(val);
// 格式化代码
const codeEditerFormat = () => codeEditer.trigger('anything', 'editor.action.formatDocument');
// 更新配置
const updateCodeEditerConfig = () => {
codeEditer.updateOptions(codeEditerConfig.value);
console.log(codeEditerConfig.value);
};
// 双向绑定,更新编辑器内容
watch(
() => props.modelValue,
(val) => {
if (val !== getCodeEditerValue()) setCodeEditerValue(val);
}
);
// 监听配置修改
watch(
() => props.config,
() => {
if (codeEditer) updateCodeEditerConfig();
},
{ immediate: true, deep: true }
);
// 监听语言配置变更
watch(
() => props.config.language,
(val) => {
if (codeEditer) monaco.editor.setModelLanguage(codeEditer.getModel(), val);
},
{ immediate: true, deep: true }
);
onMounted(() => {
initCodeEditer();
initCodeChange();
blurAndFoucsMoniter();
});
defineExpose({ codeEditerFormat });
</script>
<style lang="less" scoped>
.code-editer-wrap {
border: 1px solid #ccc;
height: 100%;
width: 100%;
}
</style>
代码自动补全Worker挂载 (./hooks/dependencies.js)
目前官方库仅支持以下语言,更多自定义配置可以查看官方文档
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?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 * as monaco from 'monaco-editor';
self.MonacoEnvironment = {
getWorker(_, label) {
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();
},
};
export default monaco;
引用组件
<script setup>
import CodeEditer from '@/component/CodeEditer/Index.vue';
import { reactive, ref } from 'vue';
const codeEditerValue = ref('');
const codeEditerConfig = reactive({
fontSize: 14,
language: 'javascript',
theme: 'vs-dark',
});
</script>
<template>
<div class="app-component">
<div class="center-box">
<h1>Code Editer Demo</h1>
<div class="opt-box">
<el-select v-model="codeEditerConfig.language" class="m-2" placeholder="Select">
<el-option
v-for="item in ['python', 'javascript', 'json', 'css', 'html', 'typescript']"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</div>
<div class="edit-area">
<div class="edit-box">
<CodeEditer v-model="codeEditerValue" class="code-editer-box" :config="codeEditerConfig"></CodeEditer>
</div>
<div class="log-box">
<el-input
v-model="codeEditerValue"
class="textarea"
:input-style="{ height: '100%' }"
type="textarea"
readonly
></el-input>
</div>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.app-component {
display: flex;
justify-content: center;
align-content: center;
h1 {
text-align: center;
}
.opt-box {
margin-bottom: 5px;
}
.edit-area {
display: flex;
width: 1400px;
height: 800px;
.edit-box {
width: 50%;
box-sizing: border-box;
border-radius: 4px;
overflow: hidden;
}
.log-box {
width: 50%;
box-sizing: border-box;
margin-left: 20px;
.textarea {
height: 100%;
font-size: 14px;
}
}
}
}
</style>