众所周知, VsCode是微软团队基于Electron编写的, 同是JS, 迁移到浏览器环境去运行自然也是非常方便的

想想第一次比较有印象看到monacoEditor是在leetcode和codesandbox, 当时看到的时候很震撼, 也很激动, 一直想试一下


这次某个项目用到了, 踩了踩坑

来个背景: 我的项目是基于create-react-app+craco搭的react项目

首先自然是看官方文档

点进去API Doc一看, 看不懂, 太多方法了, 需求也没那么复杂
我只是需要高亮, 代码补全和错误提示罢了, 并不是也不能像leetcode或codesandbox那样自定义化程度那么高

尝试clone下来原仓库

看了browser-esm-webpack-typescript-react的例子, 跟着它webpack.config.js来改, 也没有成功跑起来
这里我项目用的是create-react-app+craco, 对于不擅长webpack的我也是一件头疼的事情

这时候百度到了一篇很好的文章 闲谈Monaco Editor-基本使用

基本解决了我的疑惑, 这里挑一些重点吧

  1. Monaco的实现采用worker的方式,因为语法解析需要耗费大量时间,所以用worker来异步处理返回结果比较高效。我们使用的话需要两步。
  2. webworker暂时支持http协议, 像ftp协议是不支持的
  3. 手动修改webpack.config.js的方式会因为动态hash文件名而导致打包后的入口对不上, webwoker运行在单独线程上, 没有window变量, 需要修改webpack的全局变量, 拆分entry文件等…

那么复杂, 能不能更懒人点

还好有, 官方出了个monaco-editor-webpack-plugin的插件把这些事情干了,
这时候编辑器可以渲染出来了, 但…又出现了一个神奇的问题

Error: Unexpected usage
    at EditorSimpleWorkerImpl.BaseEditorSimpleWorker.loadForeignModule (editorSimpleWorker.js:407)
    at eval (webWorker.js:42)
    at ShallowCancelThenPromise.CompletePromise_then [as then] (winjs.base.js:1591)
    at MonacoWebWorkerImpl._getForeignProxy (webWorker.js:41)
    at MonacoWebWorkerImpl.getProxy (webWorker.js:65)
    at WorkerManager._getClient (workerManager.js:55)
    at WorkerManager.getLanguageServiceWorker (workerManager.js:74)
    at DiagnostcsAdapter.worker [as _worker] (tsMode.js:46)
    at DiagnostcsAdapter._doValidate (languageFeatures.js:158)
    at onModelAdd (languageFeatures.js:117)

不仅报错, 每次输入的时候还会有很明显的卡顿, 自然是无法忽视的问题

这时候我只能去仓库issue看看有没有同类问题
发现还蛮多的, 而且原因和解决方法千奇百怪, 最后发现了和我一样的问题
https://github.com/microsoft/monaco-editor-webpack-plugin/issues/32

还有解决方法:

For Javascript
My Webpack configuration is

new MonacoWebpackPlugin({
     languages: ['javascript', 'typescript']
})

Which fixed my issue.


哦...原来是我忘记加了typescript的配置

最后效果是这样的

附上React组件代码

import React, { useRef, useEffect, FC } from 'react'
import * as monaco from 'monaco-editor'
import { ConfigType } from '@/utils/Request/DynamicConfigSystem/Config'
import { isUndef } from '@/utils/tools'
interface MonacoEditorProps {
  value: string
  contentType: ConfigType
  darkTheme?: boolean
  // onChange?:
}

const CONFIG_TYPE_TEXT = {
  JS: 'javascript',
  JSON: 'json',
  CSS: 'css',
  HTML: 'html',
}

const MonacoEditor: FC<MonacoEditorProps> = ({
  value,
  contentType,
  darkTheme,
}) => {
  const divEl = useRef<HTMLDivElement>(null)
  let editor: monaco.editor.IStandaloneCodeEditor

  const init = () => {
    if (divEl.current) {
      editor = monaco.editor.create(divEl.current, {
        value,
        language:
          CONFIG_TYPE_TEXT[
            ConfigType[contentType] as keyof typeof CONFIG_TYPE_TEXT
          ],
        theme: darkTheme ? 'vs-dark' : 'vs',
        automaticLayout: true,
      })
      editor.onDidChangeModelContent(() => {
        // const newValue = editor.getValue()
        // console.log(newValue)
      })
    }
    return () => {
      editor.dispose()
    }
  }

  useEffect(init, [])

  if (isUndef(ConfigType[contentType])) return null
  return (
    <div
      className="Editor"
      ref={divEl}
      style= height( '100%', width: '100%' )
    />
  )
}
export default MonacoEditor