什么是 monaco-editor
Monaco编辑器是驱动VS Code的代码编辑器。它采用MIT许可证,并支持Edge、Chrome、Firefox、Safari和Opera浏览器。Monaco编辑器不支持移动浏览器或移动Web框架。
---译自《monaco-editor官方网站》
monaco
是一个开源的编辑器插件,这个编辑器功能十分强大,可以让你在浏览器中轻松的进行代码编辑。许多知名网站都用到了这个编辑器插件,如果你的网站也有使用代码编辑器的需求,强烈推荐你使用一下monaco
。当然,也有其他很多类似的编辑器插件,但 monaco
以其强大而完备的功能独占鳌头。
虽然 monaco
十分强大,与之伴随而来的是它的包也比较大,当我们去使用它的时候,会给我们的网站带来一些性能方面的影响,目前现有的关于 monaco
的使用的文章也比较少,本篇文章介绍一下笔者在对 monaco
使用方面的一些优化尝试与建议。
常规使用方式
打开 monaco-editor
文件,我们可以看到,有 esm 和 min 两个文件包,里面的目录结构也比较相似,意味着有两种引入方式,以下将会介绍。
下载包
shell
npm install monaco-editor
AMD 引入方法
html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>
<body>
<div id="container" style="width:800px;height:600px;border:1px solid grey"></div>
<script src="monaco-editor/min/vs/loader.js"></script>
<script>
require.config({ paths: { vs: 'monaco-editor/min/vs' } });
require(['vs/editor/editor.main'], function () {
var editor = monaco.editor.create(document.getElementById('container'), {
value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
language: 'javascript'
});
});
</script>
</body>
</html>
ESM 引入方法
以下代码示例为在 vite 中使用参考
js
import * as monaco from 'monaco-editor';
self.MonacoEnvironment = {
getWorker: function (workerId, label) {
const getWorkerModule = (moduleUrl, label) => {
return new Worker(self.MonacoEnvironment.getWorkerUrl(moduleUrl), {
name: label,
type: 'module'
});
};
switch (label) {
case 'json':
return getWorkerModule('/monaco-editor/esm/vs/language/json/json.worker?worker', label);
case 'css':
case 'scss':
case 'less':
return getWorkerModule('/monaco-editor/esm/vs/language/css/css.worker?worker', label);
case 'html':
case 'handlebars':
case 'razor':
return getWorkerModule('/monaco-editor/esm/vs/language/html/html.worker?worker', label);
case 'typescript':
case 'javascript':
return getWorkerModule('/monaco-editor/esm/vs/language/typescript/ts.worker?worker', label);
default:
return getWorkerModule('/monaco-editor/esm/vs/editor/editor.worker?worker', label);
}
}
};
monaco.editor.create(document.getElementById('container'), {
value: "function hello() {\n\talert('Hello world!');\n}",
language: 'javascript'
});
如果你仅需要使用其核心功能,你可以这样引入monaco ,会使得 monaco的体积减小一点
shell
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
相关的功能需要选择性的引入,比如你需要使用 monaco
展示 js
语言,并且使用注释功能,你将需要这样引入
shell
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'
import 'monaco-editor/esm/vs/editor/contrib/comment/browser/comment.js'
个人不推荐这种引入方式,因为相关文档几乎没有,会需要一些理解成本去尝试
缺点
- ESM 引入方式导致包过大:打包后的文件高至 5 到 6M,及时仅使用其核心功能,文件依旧有超过2M的体积,给网页加载带来了一定的负担
- AMD 引入的方式只能同步加载脚本文件,意味着需要等待脚本加载完毕才能使用,依然对性能造成一定的影响
CDN引入使用
伴随着 monaco
编辑器强大功能,打包出来的包体积偏大是可以理解的。由此我们可以采用 cdn 引入的方式,使得 monaco
以更快更轻便的方式加载。
免费 cdn
服务比较多,比如 cloudflare
、jsdelivr
、bootcdn
这些 CDN加速服务,以下我将以 cloudflare
作为示例。
步骤
以下为 monaco
在 vue3
中的使用
- 在 html 文件当中引入cdn资源
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>lemCloud</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/loader.js"></script>
<script defer>
require.config({
paths: {
vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/',
},
})
window.MonacoEnvironment = {
getWorkerUrl: function (workerId, label) {
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
self.MonacoEnvironment = {
baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/'
};
importScripts('https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/base/worker/workerMain.js');`)}`
},
}
require(['vs/editor/editor.main'], function () {
})
</script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
- 使用
js
monaco.editor.create(document.getElementById('container'), {
value: "function hello() {\n\talert('Hello world!');\n}",
language: 'javascript'
});
缺点
对性能造成较大影响 :在 vue3
中使用 monaco
时是在 index.html
文件中引入两个脚本文件,并且第二个脚本必须等待第一个脚本调用成功后再进行调用,无论你是否在首页使用了它,都会加载上面这些 monaco
相关的文件,意味着将会加长首屏加载时间,并非最佳实践方案。
通过 @monaco-editor/loader 使用
这是一个可以在浏览器中轻松设置 monaco-editor
的实用程序,通过其加载器脚本配置和下载 monaco
源代码,无需使用 webpack
(或任何其他模块捆绑器)的配置文件。
详细的使用步骤可以参考其文档链接,本文仅介绍其在 vue3 中的一次实践尝试。
下载包
shell
npm install @monaco-editor/loader
创建 hooks
由于这是一部分可以公用的代码,所以使用了 vue3
的 hooks
,方便 monaco
的功能可重复地使用。
创建了 initMonaco
方法异步引入 monaco
,并避免重复初始化,并且不阻塞页面的正常加载,提供了一个monacoRef
(即为前文中的 monaco),在想使用的页面中使用。
ts
// src\hooks\useMonacoEditor.ts
import loader from '@monaco-editor/loader'
import { ref } from 'vue'
const monacoRef = ref<any>(null)
const monacoLoader = loader.init()
const initMonaco = () => {
return new Promise<void>((resolve, reject) => {
if (monacoRef.value) {
resolve()
return
}
monacoLoader
.then((monacoInstance) => {
monacoRef.value = monacoInstance
})
.catch((error) => {
if (error?.type !== 'cancelation') {
console.error('Monaco initialization error:', error)
reject()
}
})
})
}
export function useMonacoEditor() {
return {
initMonaco,
monacoRef
}
}
使用
js
import { useMonacoEditor } from '@/hooks/useMonacoEditor'
const { monacoRef, initMonaco } = useMonacoEditor()
onMounted(async()=>{
await initMonaco()
monacoRef.value.editor.create(document.getElementById('container'), {
value: "function hello() {\n\talert('Hello world!');\n}",
language: 'javascript'
});
})
可以看到使用 @monaco-editor/loader
之后,下载的文件在 1M以下,并使用了 jsdelivr
的CDN加速服务,可以使用到 monaco-editor
的几乎所有功能。
总结
总的来说本篇文章是笔者对于 monaco-editor
的最佳使用方式探索尝试的总结,目前比较推荐通过 @monaco-editor/loader 引入的方式,可以异步使用,并使用 CDN服务加速,可以更方便快捷的在浏览器中使用。感谢阅读,如有疑问,欢迎留言讨论。