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

相关推荐
利刃之灵14 分钟前
02-HTML结构
前端·html
Hopebearer_20 分钟前
理解 React 的 useEffect
前端·javascript·react.js·前端框架
excel2 小时前
webpack 检出图 第 十 节 lib/ChunkGraph.js
前端
好_快2 小时前
Lodash源码阅读-parent
前端·javascript·源码阅读
shmily ....2 小时前
前端实战:基于 Vue 与 QRCode 库实现动态二维码合成与下载功能
前端·vue.js
excel2 小时前
webpack 检出图 第 九 节 lib/ChunkGraph.js
前端
好_快2 小时前
Lodash源码阅读-pullAllWith
前端·javascript·源码阅读
好_快2 小时前
Lodash源码阅读-pullAllBy
前端·javascript·源码阅读
web守墓人6 小时前
【gpt生成-其一】以go语言为例,详细描述一下 :语法规范BNF/EBNF形式化描述
前端·gpt·golang
pink大呲花9 小时前
使用 Axios 进行 API 请求与接口封装:打造高效稳定的前端数据交互
前端·vue.js·交互