在 Vite+Vue3 项目中集成 Monaco Editor

最近接到一个需求,需要将系统中的文本预览功能迭代成在线代码编辑器形式,支持行数显示、代码高亮、主题配置等功能。经调研后,选择了功能强大的 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); // 更新编辑器内容
  }
});
  

同时我们使用了节流函数来提高性能,降低不必要的开销。

性能优化:解决卡死问题

开发中发现一个问题,编辑器初始化和显示没有问题。但每当调用 getValuesetValuedispose 等方法,就会导致页面卡死。经测试后定位到是 Vue3 的代理机制导致的,解决方案是使用 toRaweditorInstance.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。原因是该插件与 ViteESM 模块规范冲突。解决方案很简单,社区有人提供了 ESM 规范的插件,将原有的插件替换成 vite-plugin-monaco-editor-esm 即可。

以上便是我搞定 Vite + Vue3 + Monaco Editor 集成的全过程。从基础配置到性能优化,解决了大文件分片加载、响应式陷阱和构建报错等问题。希望这些技巧能帮你快速上手,减少踩坑,打造高效的 Web IDE!

相关推荐
excel12 分钟前
前端加密的作用与使用场景
前端
计算机魔术师1 小时前
纽约时报版权诉讼披露:微软高管内部称训练 AI 是人类历史上最大规模劳动窃取
前端
去伪存真2 小时前
大模型的参数量为什么那么大?
前端·人工智能
IT_陈寒2 小时前
Java空指针这次真把我坑惨了
前端·人工智能·后端
八荒启·交互动画3 小时前
# Web特效020—让 Web 特效真正动起来:时间循环应该怎么接
前端·webgl·网页特效·八荒启-交互动画·八荒启
动恰客流统计3 小时前
线下零售数字化浪潮下,客流统计的3个核心发展趋势
大数据·前端·人工智能
用户938515635074 小时前
从小米前端面试题,彻底搞懂闭包、作用域链与 useState 惰性初始化
前端·面试
粥里有勺糖5 小时前
视野修炼第133期 | Native 回春了?
前端·github·agent
aichitang20245 小时前
前端小skill
前端·人工智能·算法·ai·前端框架
前端·柱子5 小时前
q-floodfill白边锯齿?一招搞定抗锯齿边缘问题
前端·html·canva可画