monaco-editor 优化使用实践

什么是 monaco-editor

Monaco编辑器是驱动VS Code的代码编辑器。它采用MIT许可证,并支持Edge、Chrome、Firefox、Safari和Opera浏览器。Monaco编辑器不支持移动浏览器或移动Web框架。

---译自《monaco-editor官方网站

monaco 是一个开源的编辑器插件,这个编辑器功能十分强大,可以让你在浏览器中轻松的进行代码编辑。许多知名网站都用到了这个编辑器插件,如果你的网站也有使用代码编辑器的需求,强烈推荐你使用一下monaco。当然,也有其他很多类似的编辑器插件,但 monaco 以其强大而完备的功能独占鳌头。

虽然 monaco 十分强大,与之伴随而来的是它的包也比较大,当我们去使用它的时候,会给我们的网站带来一些性能方面的影响,目前现有的关于 monaco 的使用的文章也比较少,本篇文章介绍一下笔者在对 monaco 使用方面的一些优化尝试与建议。

常规使用方式

打开 monaco-editor 文件,我们可以看到,有 esmmin 两个文件包,里面的目录结构也比较相似,意味着有两种引入方式,以下将会介绍。

下载包

shell 复制代码
npm install monaco-editor

AMD 引入方法

参考: github.com/microsoft/m...

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 引入方法

参考:github.com/microsoft/m...

以下代码示例为在 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'

个人不推荐这种引入方式,因为相关文档几乎没有,会需要一些理解成本去尝试

缺点

  1. ESM 引入方式导致包过大:打包后的文件高至 5 到 6M,及时仅使用其核心功能,文件依旧有超过2M的体积,给网页加载带来了一定的负担
  2. AMD 引入的方式只能同步加载脚本文件,意味着需要等待脚本加载完毕才能使用,依然对性能造成一定的影响

CDN引入使用

伴随着 monaco 编辑器强大功能,打包出来的包体积偏大是可以理解的。由此我们可以采用 cdn 引入的方式,使得 monaco 以更快更轻便的方式加载。

免费 cdn 服务比较多,比如 cloudflarejsdelivrbootcdn 这些 CDN加速服务,以下我将以 cloudflare 作为示例。

步骤

以下为 monacovue3 中的使用

  1. 在 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>
  1. 使用
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 使用

www.npmjs.com/package/@mo...

这是一个可以在浏览器中轻松设置 monaco-editor 的实用程序,通过其加载器脚本配置和下载 monaco 源代码,无需使用 webpack(或任何其他模块捆绑器)的配置文件。

详细的使用步骤可以参考其文档链接,本文仅介绍其在 vue3 中的一次实践尝试。

下载包

shell 复制代码
npm install @monaco-editor/loader

创建 hooks

由于这是一部分可以公用的代码,所以使用了 vue3hooks,方便 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服务加速,可以更方便快捷的在浏览器中使用。感谢阅读,如有疑问,欢迎留言讨论。

相关推荐
zqx_73 分钟前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己20 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称43 分钟前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色1 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2341 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河1 小时前
CSS总结
前端·css
BigYe程普2 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H2 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai2 小时前
网站开发的发展(后端路由/前后端分离/前端路由)
前端