最近尝试了一下在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-editor
和monaco-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。