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服务加速,可以更方便快捷的在浏览器中使用。感谢阅读,如有疑问,欢迎留言讨论。

相关推荐
一个处女座的程序猿O(∩_∩)O1 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink4 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者6 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-6 小时前
验证码机制
前端·后端
燃先生._.7 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖8 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235248 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240259 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar9 小时前
纯前端实现更新检测
开发语言·前端·javascript