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

相关推荐
热爱编程的小曾21 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin33 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox1 小时前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox2 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞2 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行2 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758102 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox