本教程适用于基于
webpack
打包的React
SPA 应用 .
我在使用 create-react-app
创建的 React
开发环境中, 曾使用过 monaco-editor-webpack-plugin
插件来进行 monaco editor
的开发和部署,发现总是有一些 Error 提示,虽然不影响使用,但总是不太美观, 经历千辛万苦,终于完美的解决了这个问题, 在此记录下。
此文有参考 Short guide to using Monaco with Create React App , 注:查看原文需科学上网
解决此问题的关键在于 index.html
所在的域名 和 css, js
所在的域名必须相同
因为 moncao editor
再使用的时候回自动去缓存 serverWorker
,因此 index.html
所在的域名 和 css, js
所在的域名必须相同,否则调用 serverWorker
会被禁止,虽然不影响使用,但总是不太美观,总是有一些 Error 提示。
但是 index.html
所在的域名 和 css, js
所在的域名在同一个服务器时会影响服务器的整体带宽(服务器的带宽还是蛮贵的哦,哈哈),
所幸的是总算解决了,以下是解决方法(以本站为例):
准备工作
安装 monaco editor.
目前的最新版本是
0.21.2
yarn add monaco-editor
拷贝monaco editor
把 node_modules
的 monaco editor
拷贝出来, 放到public
文件夹下,把 monaco editor
的静态文件托管起来,用浏览器测试一下确认下文件可访问
monaco editor
文件夹结构,请自行拼装。
例如本站托管的地址,测试下
https://cdn.jansora.com/lib/monaco-editor/0.21.2/min/vs/base/worker/workerMain.js
配置 monaco editor
- 在
public/index.html
的<head></head>
中添加以下内容
<script type="text/javascript" src="https://cdn.jansora.com/lib/monaco-editor/0.21.2/min/vs/loader.js"></script>
<script>
window.MonacoEnvironment = {
getWorkerUrl: function (workerId, label) {
return '/monaco-editor-worker-loader-proxy.js';
}
};
</script>
- 在
public
文件夹下创建monaco-editor-worker-loader-proxy.js
文件,添加以下内容
self.MonacoEnvironment = {
baseUrl: 'https://cdn.jansora.com/lib/monaco-editor/0.21.2/min/'
}
importScripts('https://cdn.jansora.com/lib/monaco-editor/0.21.2/min/vs/base/worker/workerMain.js') // eslint-disable-line
就这么简单,其他的什么都不用配置
使用 monaco editor
以下以 React hooks
的方式,显示一下 CodeEditor
和 DiffEditor
的使用方法.
CodeEditor
const CodeEditor = (props) => {
const {value, onChange, id, force} = props;
const language = props.language ? props.language : 'javascript';
const ref = useRef(null);
const style = props.style ? props.style : {};
const options = props.options ? props.options : {};
const [model, setModel] = useState(monaco.editor.createModel(value,"javascript"));
const [editor, setEditor] = useState(null);
useEffect(() => {
if(ref.current) {
setEditor(
monaco.editor.create(ref.current, {
model, ...options, language
})
)
}
}, [ref, model, language]);
useEffect(() => {
if (editor) {
editor.setModelLanguage(model, language)
}
}, [language, model])
useEffect(() => {
if(editor && force) {
model.setValue(value);
// model
}
}, [value, force, model]);
useEffect(() => {
if(editor) {
editor.onDidChangeModelContent((event) => onChange && onChange(model.getValue()))
}
}, [editor]);
useEffect(()=> {
return () => model.dispose()
}, [])
return (
<div id="monaco" ref={ref} style={style} />
)
}
export default CodeEditor;
DiffEditor
/*
* @file DiffEditor.jsx
* @author jansora
* @date 2020/2/12
*/
import React, {useEffect, useRef, useState} from "react";
import * as monaco from 'monaco-editor/esm/vs/editor/editor.main';
const DiffEditor = (props) => {
const {original, modified} = props;
const ref = useRef(null);
const style = props.style ? props.style : {};
const [editor, setEditor] = useState(null);
useEffect(() => {
if(ref.current) {
setEditor(monaco.editor.createDiffEditor(
ref.current, {theme: "vs", readOnly: true})
)
}
}, [ref]);
useEffect(()=> {
if(editor) {
editor.setModel({
original: monaco.editor.createModel(original.data, original.language),
modified: monaco.editor.createModel(modified.data, modified.language)
});
}
}, [editor, modified, original])
return (
<div id="monaco" ref={ref} style={style}/>
)
}
export default DiffEditor;
使用方法
<DiffEditor
modified={{data: raw, language: 'markdown'}}
original={{data: history.raw, language: 'markdown'}}
/>
<CodeEditor
force={true} // 如果代码编辑器的值是受外部控制的, force设为 true, 如果是非受控的, 请改为force=false, 并在绑定onChange时间去获取editor的值
language="json"
value={"{\n" +
"\t\"a\": 1\n" +
"}"}
onChange={(value) => console.log(value)}
style={{height: 400}}
/>