React 引用 monaco editor

本教程适用于基于 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_modulesmonaco editor 拷贝出来, 放到public文件夹下,把 monaco editor 的静态文件托管起来,用浏览器测试一下确认下文件可访问

monaco editor 文件夹结构,请自行拼装。

例如本站托管的地址,测试下
https://cdn.jansora.com/lib/monaco-editor/0.21.2/min/vs/base/worker/workerMain.js

配置 monaco editor

  1. 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>
  1. 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 的方式,显示一下 CodeEditorDiffEditor 的使用方法.

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}}
/>

评论栏