How to: 在electron中嵌入vscode的编辑体验

最近尝试了一下在electron中使用monaco-editor嵌入一个vscode体验的输入框。这篇文章梳理一下过程。

0x00 概览

这次,我会用electron-react-boilerplate创建一个基于React的electron工程,然后嵌入一个monaco-react封装好的monaco-editor组件。说起来很简单,但实际操作过程中踩了非常多的坑。

0x01 准备工作

首先用electron-react-boilerplate创建一个空工程。就叫electron-monaco好了。我个人比较喜欢yarn,所以下面使用的包管理器都是yarn

git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git electron-monaco
cd electron-monaco
yarn install

之后,我们安装monaco-editormonaco-react。由于集成monaco-editor很麻烦(参见这里),我们使用monaco-editor-webpack-plugin这个Webpack插件来简化配置。

yarn add monaco-editor @monaco-editor/react
yarn add -D monaco-editor-webpack-plugin

第一个需要注意的地方就是monaco-editor-webpack-plugin需要安装为dev dependency。原因是在postinstall过程中erb会安装production dependencies,而monaco-editor-webpack-plugin需要的依赖在production dependencies中不存在,导致类似下面的错误:

ERROR in ./node_modules/terser-webpack-plugin/dist/utils.js 409:6-26
Module not found: Error: Can't resolve 'uglify-js' in '/Users/direwolf/electron-monaco/node_modules/terser-webpack-plugin/dist'
 @ ./node_modules/terser-webpack-plugin/dist/index.js 22:4-22
 @ ./node_modules/webpack/lib/config/defaults.js 1164:25-57
 @ ./node_modules/webpack/lib/index.js 337:10-66
 @ ./node_modules/monaco-editor-webpack-plugin/out/index.js 206:77-95
 @ dll renderer renderer[5]

0x02 集成monaco-editor

接下来,编辑webpack配置。我们用erb创建的这个工程,webpack配置位于.erb/configs/webpack.config.base.ts,修改这里可以同时作用于所有位置。需要添加的是plugins这里:

/**
 * Base webpack config used across other specific configs
 */

// ... other imports
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';

const configuration: webpack.Configuration = {
  // ...
  plugins: [
    // ...
    new MonacoWebpackPlugin(),
  ],
};

export default configuration;

0x03 配置monaco-react

由于monaco-react默认从CDN加载monaco-editor,而electron应用默认禁止加载外部来源,如果直接使用monaco-react给的组件会出现卡在Loading界面的问题。我们需要让它去加载node_modules里的monaco-editor这个Issue给出了很多方案,经我测试都有或多或少的问题。我直接说我试下来效果最好的一种了。

首先在/src/renderer下新建一个MonacoEditor.tsx。这一步是为了隔离,不让HMR热重载的时候更新monaco相关的部分。monaco和HMR兼容性不太好,如果直接在页面里写会让整个页面的热重载炸掉!顺便,还可以作一些配置,方便重用组件。这个tsx的内容如下:

import * as monaco from 'monaco-editor';
import OriginalMonaco, { EditorProps, loader } from '@monaco-editor/react';
import React from 'react';

loader.config({ monaco });

export const MonacoEditor: React.FC<EditorProps> = (props) => {
  return (
    <OriginalMonaco
      height="75vh"
      width="75vw"
      defaultLanguage="javascript"
      theme="vs-dark"
      defaultValue="console.log('Hello, monaco!')"
      // eslint-disable-next-line react/jsx-props-no-spreading
      {...props}
    />
  );
};

export default MonacoEditor;

最关键的部分在于loader.config({ monaco });,它将monaco-editor传给monaco-react,相当于把活丢给了webpack去处理。

0x04 完成

最后就简单了,直接在页面里调用封装好的这个组件就行。我将自带的Hello组件改成了这样:

function Hello() {
  return (
    <div>
      <MonacoEditor />
    </div>
  );
}

最终,我们就得到了一个集成了monaco-editor的electron app。

结果

本文链接:

https://www.direcore.xyz/archives/46/
1 + 4 =
快来做第一个评论的人吧~