最近接到一个需求,需要将系统中的文本预览功能迭代成在线代码编辑器形式,支持行数显示、代码高亮、主题配置等功能。经调研后,选择了功能强大的 Monaco Editor。作为 VS Code 背后的代码编辑器核心,完成这些功能绰绰有余,后续也很方便扩展新的功能。
本以为照着教程做很简单,但在开发过程中,还是遇到了一些问题。现将开发的重点和问题的解决方案整理如下,希望对大家有帮助。
安装和初始化
bash
# 安装依赖
pnpm i monaco-editor
ts
// 初始化
import { ref, onMounted, watch, onBeforeUnmount } from 'vue';
import * as monaco from 'monaco-editor';
const editorContainer = ref(null); // 绑定容器元素节点
const editorInstance = ref(null);
const languageMap = {
js: 'javascript',
ts: 'typescript',
// 其他语言映射...
};
// 创建编辑器实例
onMounted(() => {
editorInstance.value = monaco.editor.create(editorContainer.value, {
value: props.value,
language: languageMap[props.language] || 'plaintext',
theme: 'vs-dark',
automaticLayout: true, // 窗口resize时自适应
readOnly: true, // 是否设置为只读模式
});
});
// 组件卸载时销毁实例
onBeforeUnmount(() => {
if (editorInstance.value) {
editorInstance.value.dispose();
}
});
设置好容器,传入文本内容和对应的代码语言参数,初始化后,编辑器就会显示在页面中了。
大文件分片加载优化
因项目中可能会有较大体积的文本预览需求,需要做文件分片加载和读取。之前的版本是监听 scroll
事件来实现的,在 Monaco Editor 中,也提供了类似的函数支持。
ts
// 获取编辑器的配置选项
const options = editorInstance.value.getOptions();
// 获取行高
const lineHeight = options.get(monaco.editor.EditorOption.lineHeight);
// 节流函数
function throttle(func, wait) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= wait) {
lastTime = now;
func.apply(this, args);
}
};
}
// 监听滚动事件
editorInstance.value.onDidScrollChange(throttle((e) => {
const layoutInfo = editorInstance.value.getLayoutInfo();
const scrollTop = e.scrollTop; // 当前滚动位置
const visibleHeight = layoutInfo.height; // 可见区域的高度
const model = editorInstance.value.getModel();
const lineCount = model.getLineCount(); // 总行数
// 判断是否滑到底部
if (scrollTop >= lineCount * lineHeight - visibleHeight) {
console.log('滚动条滑到底部了!');
emit('load-more'); // 发出 load-more 事件
}
}, 1000)); // 1000ms 的节流间隔
// 监听 value 变化,加载新的文本内容
watch(() => props.value, (newValue) => {
console.log('value changed:', newValue.length);
if (editorInstance.value) {
const oldValue = editorInstance.value.getValue(); // 获取编辑器内容
editorInstance.value.setValue(oldValue + newValue); // 更新编辑器内容
}
});
同时我们使用了节流函数来提高性能,降低不必要的开销。
性能优化:解决卡死问题
开发中发现一个问题,编辑器初始化和显示没有问题。但每当调用 getValue
、setValue
、dispose
等方法,就会导致页面卡死。经测试后定位到是 Vue3
的代理机制导致的,解决方案是使用 toRaw
对 editorInstance.value
进行包装。
在 Vue3
中,响应式系统会通过 Proxy
对对象进行深层代理,当直接操作被代理的 Monaco Editor 实例时,每次调用 setValue
等方法都会触发 Proxy
的拦截和依赖追踪,这种额外的代理开销在频繁操作大量数据时会形成性能黑洞。而 toRaw
通过剥离代理层直接操作原始编辑器实例,避免了响应式系统的性能损耗,如同卸下编辑器身上的"监控枷锁",使得操作性能回归原生级别。
ts
import { ref, onMounted, watch, onBeforeUnmount, toRaw } from 'vue';
const oldValue = toRaw(editorInstance.value).getValue();
toRaw(editorInstance.value).setValue(oldValue + newValue);
toRaw(editorInstance.value).dispose();
解决 vite-plugin-monaco-editor 引入报错
使用 vite-plugin-monaco-editor
插件可以简化 Vite
加载 Monaco Editor 的过程。
ts
// vite.config.ts
import { defineConfig } from 'vite';
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
export default defineConfig({
plugins: [monacoEditorPlugin()],
});
但是在项目开发过程中发现,引入该插件会报错:TypeError: monacoEditorPlugin is not a function
。原因是该插件与 Vite
的 ESM
模块规范冲突。解决方案很简单,社区有人提供了 ESM
规范的插件,将原有的插件替换成 vite-plugin-monaco-editor-esm
即可。
以上便是我搞定 Vite
+ Vue3
+ Monaco Editor 集成的全过程。从基础配置到性能优化,解决了大文件分片加载、响应式陷阱和构建报错等问题。希望这些技巧能帮你快速上手,减少踩坑,打造高效的 Web IDE!