Monaco 编辑器的使用

1,746 阅读2分钟

前言

对于 Monaco 这个编辑器我在两个项目中都用到了,第一个项目是 Open-Lineage,体验地址:openbytecode.com/openLineage… 效果如下:

image.png

第二个项目就是 OpenByteCode,体验地址:openbytecode.com/

编辑器

市面上编辑器有很多,由于我这里选择了 Monaco,所以本文主要讲 Monaco 的使用方法。

在 react 中使用方法(使用 webpack 打包)

  1. 安装依赖
yarn add react-monaco-editor
  1. 安装插件
yarn add monaco-editor-webpack-plugin -D
  1. 安装重写 webpack 配置依赖
yarn add react-app-rewired -D
  1. 创建如下文件 config-overrides.js 覆盖默认的 webpack plugin 配置
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = function override(config, env) {
  if (!config.plugins){
    config.plugins = [];
  }
  config.plugins.push(new MonacoWebpackPlugin({
    languages: ["sql", "json", "javascript", "typescript"],
  }))

  return config;
};
  1. 编写组件
import { useRef, useState } from 'react';  
import MonacoEditor from 'react-monaco-editor';  
  
interface SqlEditorProps {  
theme?: string;  
}  
  
const SqlEditor = ({ theme }: SqlEditorProps) => {  
    const editorRef = useRef();  
    const [code, setCode] = useState('select * from ');  
    const options: any = {  
        // selectOnLineNumbers: true,  
        // roundedSelection: false,  
        // readOnly: false,  
        // cursorStyle: 'line',  
        // automaticLayout: true,  
        // selectOnLineNumbers: true,  
        // renderSideBySide: false,  
        // scrollBeyondLastLine: false,  
        // formatOnPaste: true,  
        // automaticLayout: true,  
        // contextmenu: false, // 禁止右键  
        // fixedOverflowWidgets: true, // 超出编辑器大小的使用fixed属性显示  
        // quickSuggestions: true, // 默认的提示关掉  
        // minimap: {  
        // // 缩略图  
        // enabled: false,  
        // },  
        // scrollbar: {  
        // // 滚动条  
        // horizontalScrollbarSize: 6,  
        // verticalScrollbarSize: 6,  
        // },  
        // lineNumbersMinChars: 3, // 最少显示3位长的行号  
        // lineNumbers: 'on', // 是否显示行号  
    };  
  
    const editorDidMount = (editor: any, monaco: any) => {  
    console.log('editorDidMount', editor);  
    editor.focus();  
    };  
    const onChange = (newValue: any, e: any) => {  
    console.log('onChange', newValue, e);  
    };  
  
    return (  
        <>  
            <MonacoEditor  
                width='340'  
                height='600'  
                language='sql'  
                theme={theme || 'vs-light'}  
                value={code}  
                options={options}  
                onChange={onChange}  
                //editorDidMount={editorDidMount}  
            />  
        </>  
    );  
};  
  
export default SqlEditor;
  1. 使用组件
<div className='layout-sider-edit'>  
    <SqlEditor theme={theme} />  
</div>

在 react 中使用方法(使用 Vite 打包)

  1. 安装依赖
yarn add monaco-editor

注意:这回就不需要安装其他插件了

  1. 编写组件
import { useEffect, useMemo, useRef, useState } from 'react';  
import * as monaco from 'monaco-editor';  
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';  
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';  
import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';  
import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';  
import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';  
import { MonacoEditorProps } from './types';  
import { noop, processSize } from './utils';  
  
(self as any).MonacoEnvironment = {  
    getWorker(_: any, label: any) {  
        if (label === 'json') {  
        return new JsonWorker();  
        }  
        if (label === 'css' || label === 'scss' || label === 'less') {  
        return new CssWorker();  
        }  
        if (label === 'html' || label === 'handlebars' || label === 'razor') {  
        return new HtmlWorker();  
        }  
        if (label === 'typescript' || label === 'javascript') {  
        return new TsWorker();  
        }  
        return new EditorWorker();  
    },  
};  
  
const MonacoEditor = ({  
    width,  
    height,  
    value,  
    defaultValue,  
    language,  
    theme,  
    options,  
    onChange,  
    className,  
}: MonacoEditorProps) => {  
    const containerElement = useRef<HTMLDivElement | null>(null);  

    const editor = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);  

    const fixedWidth = processSize(width || '');  

    const fixedHeight = processSize(height || '');  

    const style = useMemo(  
        () => ({  
        width: fixedWidth,  
        height: fixedHeight,  
        }),  
        [fixedWidth, fixedHeight]  
    );  

    const initMonaco = () => {  
        const finalValue = value !== null ? value : defaultValue;  
        if (!editor.current && containerElement.current) {  
            const finalOptions = { ...options };  
            editor.current = monaco.editor.create(containerElement.current, {  
            value: finalValue,  
            language,  
            ...(className ? { extraEditorClassName: className } : {}),  
            ...finalOptions,  
            ...(theme ? { theme } : {}),  
            });  
            editor.current.onDidChangeModelContent((event) => {  
            onChange && onChange(editor.current?.getValue() || '', event);  
            });  
        }  
    };  

    useEffect(() => {  
        initMonaco();  
        editor.current?.focus();  
    }, []);  

    useEffect(() => {  
        editor.current?.layout();  
    }, [width, height]);  

    useEffect(() => {  
        monaco.editor.setTheme(theme || '');  
    }, [theme]);  

    useEffect(() => {  
        if (editor.current) {  
        const model: any = editor.current.getModel();  
        monaco.editor.setModelLanguage(model, language || '');  
        }  
    }, [language]);  

    return (  
        <div  
            ref={containerElement}  
            style={style}  
        />  
    );  
};  
  
MonacoEditor.defaultProps = {  
    width: '100%',  
    height: '100%',  
    value: null,  
    defaultValue: '',  
    language: 'javascript',  
    theme: null,  
    options: {},  
    onChange: noop,  
    className: null,  
};  
  
export default MonacoEditor;
  1. 使用组件
<div className='layout-sidebar-edit'>  
    <MonacoEditor  
        width='340'  
        height='600'  
        language='sql'  
        theme={theme}  
        value={code}  
        onChange={(value) => setCode(value)}  
    />  
</div>

在 NextJs 中使用方法(使用 turbopack 打包)

  1. 安装依赖
pnpm install @monaco-editor/react

注意:这回也不需要安装其他插件

  1. 编写组件
import MonacoEditor from '@monaco-editor/react';  
  
interface EditorCodeProps {  
    lang?: string;  
    theme: string;  
    input: string;  
    setInput: (value: any) => void;  
}  
  
const EditorCode = ({  
    lang = 'markdown',  
    theme,  
    setInput,  
    input,  
}: EditorCodeProps) => {  
    return (  
        <div className='absolute inset-0 h-full w-full bg-wash dark:bg-[#1e293b]'>  
            <MonacoEditor  
                onChange={setInput}  
                theme={theme}  
                defaultLanguage={lang}  
                defaultValue={input}  
                options={{  
                    // https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IEditorConstructionOptions.html  
                    minimap: {  
                    enabled: false,  
                    },  
                    lineNumbers: 'on',  
                    scrollBeyondLastLine: false,  
                    hideCursorInOverviewRuler: true,  
                    matchBrackets: 'never',  
                    overviewRulerBorder: false,  
                    renderLineHighlight: 'none',  
                    wordWrap: 'on',  
                    tabSize: 2,  
                }}  
            />  
        </div>  
    );  
};  
  
export default EditorCode;
  1. 使用组件
<div className='flex flex-auto border-t border-gray-200 dark:border-white/10'>  
    <EditorCode  
        setInput={setInput}  
        theme={editorTheme}  
        input={input}  
    />  
</div>

总结

上面我按照不同的打包工具分别讲了 Monaco 编辑器的使用方法,当然,如果是用 Vite 打包的话,那么也是可以用第三种方案的