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

    评论栏