目录
Monaco Editor
微软之前有个项目叫做Monaco Workbench,后来这个项目变成了VSCode。
而Monaco Editor(下文简称monaco)就是从这个项目中成长出来的一个web编辑器,他们很大一部分的代码(monaco-editor-core)都是共用的,所以monaco和VSCode在编辑代码,交互以及UI上几乎是一摸一样的,有点不同的是,两者的平台不一样,monaco基于浏览器,而VSCode基于electron,所以功能上VSCode更加健全,并且性能比较强大。
注: electron --- 使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序
react 中使用react-monaco-editor
安装
yarn add react-monaco-editor
使用方法
import React from 'react';
import { render } from 'react-dom';
import MonacoEditor from 'react-monaco-editor';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
code: '// type your code...',
}
}
editorDidMount(editor, monaco) {
console.log('editorDidMount', editor);
editor.focus();
}
onChange(newValue, e) {
console.log('onChange', newValue, e);
}
render() {
const code = this.state.code;
const options = {
selectOnLineNumbers: true
};
return (
<MonacoEditor
width="800"
height="600"
language="javascript"
theme="vs-dark"
value={code}
options={options}
onChange={::this.onChange}
editorDidMount={::this.editorDidMount}
/>
);
}
}
render(
<App />,
document.getElementById('root')
);
Webpack 配置
在Umijs 2.xx中使用, 项目配置config.js中 --- plugins 中的 dll , 会导致 补全 提示 颜色...失效,所以用umijs3 ,或者不配置dll
yarn add monaco-editor-webpack-plugin -D
- 安装webpack 插件,添加 monaco-editor-webpack-plugin 到 webpack.config.js 中
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
plugins: [
new MonacoWebpackPlugin({
// available options are documented at https://github.com/Microsoft/monaco-editor-webpack-plugin#options
languages: ['json']
})
]
};
插件与编辑器版本的对应关系
monaco-editor-webpack-plugin | monaco-editor |
---|---|
3.*.* | 0.22.* , 0.23.* |
2.*.* | 0.21.* |
1.9.* | 0.20.* |
1.8.* | 0.19.* |
1.7.* | 0.18.* |
备注:Monaco Editor在内部使用CSS导入,如果您在项目中使用CSS模块化,您可能会遇到冲突。为了避免这种情况-为应用程序和monaco-editor软件包再分离出一个css的配置为其提供css-loader:
// Specify separate paths
const path = require('path');
const APP_DIR = path.resolve(__dirname, './src');
const MONACO_DIR = path.resolve(__dirname, './node_modules/monaco-editor');
{
test: /\.css$/,
include: APP_DIR,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
options: {
modules: true,
namedExport: true,
},
}],
},
{
test: /\.css$/,
include: MONACO_DIR,
use: ['style-loader', 'css-loader'],
}
属性
下列属性都是可配置的
- width --- 默认100%
- height --- 默认100%
- value --- 编辑器中要展示的代码
- defaultValue --- 编辑器中初始化展示的代码
- language --- 编辑器支持的语言格式
- theme --- 主题
- options --- 请参阅 Monaco 官方API 接口文档
- overrideServices --- 请参阅 Monaco 官方编辑器设置APi
- onChange(newValue, event) --- 当编辑器内容发生变更触发事件
- editorWillMount(monaco) --- 编辑器挂载之前发生的事件(类似于 componetWillMount React)
- editorDIdMount(monaco) --- 编辑器挂载时发生从事件(类似于 componetDidMount React)
获取 monac
可以通过导入的方式获取 monaco
import { monaco } from 'react-monaco-editor';
开发示例---自定义提示
import React, { useState } from 'react';
import MonacoEditor from 'react-monaco-editor';
import tipData from './echarts_word.js';
const initStr = ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n');
export default function SetMonaco(props) {
const [code, setCode] = useState(initStr);
const { suggestions } = props;
//编辑器默认配置项
const _initOptions = {
selectOnLineNumbers: true,
renderSideBySide: false,
// scrollBeyondLastLine: false,
// formatOnPaste: true,
// contextmenu: false, // 禁止右键
// fixedOverflowWidgets: true, // 超出编辑器大小的使用fixed属性显示
// quickSuggestions: false, // 默认的提示关掉
// minimap: {
// // 缩略图
// enabled: false,
// },
// scrollbar: {
// // 滚动条
// horizontalScrollbarSize: 6,
// verticalScrollbarSize: 6,
// },
// lineNumbersMinChars: 3, // 最少显示3位长的行号
// lineNumbers: 'on', // 是否显示行号
// wordWrap: 'on', // 是否可以换行
};
//提示文字默认配置
const _initSuggestions = formatTipData(tipData);
function _initChange(newValue, e) {
// console.log(newValue);
setCode(newValue);
props.getCode && props.getCode(newValue);
}
//输入之后的回调
const _editorDidMountHandle = (editor, monaco) => {
let sug = _initSuggestions;
if (suggestions instanceof Array && suggestions.length) {
//检测提示信息的格式
let flag = true;
suggestions.forEach((item, index) => {
if (!item.label) {
console.error(`自定义提示数组-序列${index}缺少label字段`);
flag = false;
}
if (!item.insertText) {
console.error(`自定义提示数组-序列${index}项缺少insertText字段`);
flag = false;
}
});
//如果没有问题合并
if (flag) {
sug = _initSuggestions.concat(suggestions);
}
}
monaco.languages.registerCompletionItemProvider('javascript', {
provideCompletionItems() {
return {
suggestions: sug.map((item) => {
return {
label: item.label,
kind: item.kind ? item.kind : monaco.languages.CompletionItemKind.Text,
insertText: item.label, // 实际粘贴上的值
detail: item.detail,
};
}),
};
},
triggerCharacters: [':'], // 触发提示的字符,可以写多个
});
editor.focus();
};
return (
<MonacoEditor
width="100%"
height="100%"
language="javascript"
defaultValue={code}
theme="vs-dark"
options={_initOptions}
onChange={_initChange}
editorDidMount={_editorDidMountHandle}
{...props}
/>
);
}
//转换自定义提示
function formatTipData(ary) {
if (ary instanceof Array && ary.length) {
return ary.map((item) => ({
label: item,
insertText: item,
detail: '',
}));
}
}
自定义主题
import React, { useState } from 'react';
import MonacoEditor, {monaco}from 'react-monaco-editor';
const initStr = ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n');
export default function SetMonaco(props) {
const [code, setCode] = useState(initStr);
//自定义配置主题
monaco.editor.defineTheme('myTheme', {
base: 'vs',
inherit: true,
rules: [{ background: 'EDF9FA' }],
colors: {
'editor.foreground': '#000000',
'editor.background': '#EDF9FA',
'editorCursor.foreground': '#8B0000',
'editor.lineHighlightBackground': '#0000FF20',
'editorLineNumber.foreground': '#008800',
'editor.selectionBackground': '#88000030',
'editor.inactiveSelectionBackground': '#88000015'
}
});
monaco.editor.setTheme('myTheme');
//编辑器默认配置项
const _initOptions = {
selectOnLineNumbers: true,
renderSideBySide: false
};
return (
<MonacoEditor
width="100%"
height="100%"
language="javascript"
defaultValue={code}
theme="myTheme"
options={_initOptions}
/>
);
}