众所周知, 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-基本使用
基本解决了我的疑惑, 这里挑一些重点吧
- Monaco的实现采用worker的方式,因为语法解析需要耗费大量时间,所以用worker来异步处理返回结果比较高效。我们使用的话需要两步。
- webworker暂时支持http协议, 像ftp协议是不支持的
- 手动修改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
/>
)
}
export default MonacoEditor