React 引用 monaco editor预计阅读时间 3 分钟

    The Monaco Editor is the code editor that powers VS Code. A good page describing the code editor's features is here. It is licensed under the MIT License and supports Classic Edge, Edge, Chrome, Firefox, Safari and Opera. The Monaco editor is not supported in mobile browsers or mobile web frameworks.

    本教程适用于基于 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}}
    />
    

    评论栏