在开发一个在页面上提供输入并保存js代码的编辑器功能时,考查了多个在线编辑器均不满意。因为在考察时,会不由自主的将这些编辑器和VSCode进行对比。了解到monaco编辑器其实是VSCode的网页版本时,马上决定就是它了。
先看看集成后的效果:
官网的文档api太多,短时间是很难了解到一个大概。又搜看了多篇 monaco 的安装和使用文章,居然没有一篇能够完整在本地正常运行的。无奈只能多次尝试,现在把集成要点说明如下,希望可以帮助需要的同学少走点弯路。
一、monaco 安装
本地环境: mbp m1 macos14 + vscode1.84.2 + npm9.5.1 + node18.16 + vue3.3.8
csharp
yarn add monaco-editor
或者
npm install monaco-editor
二、monaco 配置
我们只需要做到一点即可:在全局对象 Window 中定义一个 monaco 运行时所需要的一个属性 MonacoEnvironment 并定义 getWorker 方法,根据编辑器的用途,定义其内容,如下:
typescript
import { boot } from 'quasar/wrappers';
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';
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
export default boot(async () => {
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === 'json') {
return new jsonWorker();
}
if (['css', 'scss', 'less'].includes(label)) {
return new cssWorker();
}
if (['html', 'handlebars', 'razor'].includes(label)) {
return new htmlWorker();
}
if (['typescript', 'javascript', 'ts', 'js'].includes(label)) {
return new tsWorker();
}
return new EditorWorker();
},
};
});
上面的代码是quasar框架中,在系统启动时执行这段代码的一个写法。 getWorker方法内部是根据不同的label的值,来调用不同的解析器。
typescript
self.MonacoEnvironment
self就是代表Window对象。本质上其实就是定义了一个名为 MonacoEnvironment 的对象,在其中定义了getWorker方法。monaco编辑器在运行时,会去全局对象Window中寻找 MonacoEnvironment 对象,并调用 getWorker 方法来解析和处理编辑器内的代码。
三、使用
3.1 声明
在模板中,必须给定一个初始高度,比如 30vh。
vue
模板中
<div ref="codeEditBox" :style="{ height: height }" />
script中
// div的引用
const codeEditBox = ref();
// 声明一个编辑器对象
let editor: monaco.editor.IStandaloneCodeEditor;
3.2 初始化
typescript
interface IProps {
modelValue?: string;
language?: string;
theme?: string;
// 编辑器区域高度,不包括窗口的header、footer
height?: string;
}
const props = withDefaults(defineProps<IProps>(), {
modelValue: '',
language: 'javascript',
theme: 'vs-dark',
height: '30vh',
});
function init(initValue = '') {
visible.value = true;
if (initValue !== '') {
value.value = initValue;
}
console.debug('创建 monaco 编辑器');
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: false
})
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true
})
editor = monaco.editor.create(codeEditBox.value, {
value: value.value,
language: props.language,
theme: props.theme,
foldingStrategy: 'indentation',
automaticLayout: true,
contextmenu: true,
minimap: {
enabled: false,
},
scrollBeyondLastLine: false,
overviewRulerBorder: false
})
// 监听值的变化
editor.onDidChangeModelContent(() => {
const value = editor.getValue() //给父组件实时返回最新文本
emit('update:modelValue', value)
})
emit('editor-mounted', editor)
visible.value = false;
}
相关api的详细说明,可以去官网查看。不过有一个功能点比较实用 - 格式化
当我们通过编辑器打开输入的代码后,会发现这些代码是在一行的,居然没有格式化输出!这对于天天使用vscode+prettier的我们来说,无疑有点抓狂。研究了官网api文档,编写一个功能函数:
typescript
// 格式化代码,如果在init方法后调用,则需要增加100ms延时
function formatCode() {
if (editor) {
const range = editor.getModel()?.getFullModelRange();
editor.getAction('editor.action.formatDocument')?.run(range);
visible.value = false;
}
}
至此,就完成了monaco的集成和使用。