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

相关推荐
水银嘻嘻4 小时前
12 web 自动化之基于关键字+数据驱动-反射自动化框架搭建
运维·前端·自动化
小嘟嚷ovo5 小时前
h5,原生html,echarts关系网实现
前端·html·echarts
十一吖i5 小时前
Vue3项目使用ElDrawer后select方法不生效
前端
只可远观5 小时前
Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
前端·flutter
周胡杰5 小时前
组件导航 (HMRouter)+flutter项目搭建-混合开发+分栏效果
前端·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
敲代码的小吉米6 小时前
前端上传el-upload、原生input本地文件pdf格式(纯前端预览本地文件不走后端接口)
前端·javascript·pdf·状态模式
是千千千熠啊6 小时前
vue使用Fabric和pdfjs完成合同签章及批注
前端·vue.js
九月TTS6 小时前
TTS-Web-Vue系列:组件逻辑分离与模块化重构
前端·vue.js·重构
我是大头鸟7 小时前
SpringMVC 内容协商处理
前端
Humbunklung7 小时前
Visual Studio 2022 中添加“高级保存选项”及解决编码问题
前端·c++·webview·visual studio