在 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!

相关推荐
谭光志11 小时前
Vibe Coding 时代,程序员该何去何从
前端·后端·ai编程
仿生狮子12 小时前
别再说“全栈”了,AI 时代团队只认这 5 种人
前端·人工智能·后端
kyriewen13 小时前
AI 写的 React 组件,合并前必查的 6 个坏味道——每个都有代码对比
前端·javascript·react.js
Highcharts.js13 小时前
软件开发公司为什么选择 Highcharts?
前端·前端框架·echarts·数据可视化·技术选型·highcharts·图表工具
阿祖zu13 小时前
企业 AI 转型之痛,个人提效十倍不止,组织效率仍止步不前?
前端·aigc·agent
摇滚侠15 小时前
SpringBoot3+Vue3 全套视频教程 45-51
前端·javascript·vue.js
酸梅果茶15 小时前
【6】lightning_lm项目-LIO 前端 -点云预处理模块
前端·slam
Hilaku15 小时前
前端大裁员背后的恐慌:我们还剩什么底牌?
前端·javascript·程序员
咖啡无伴侣16 小时前
unplugin-auto-import 使用详解+面试高频考点
前端·架构
搬砖记录员16 小时前
录屏文件打不开?H.265兼容性踩坑与4种实战方案
前端