MonacoEditor 加载很慢该怎么优化?

2,158 阅读5分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第14篇文章,点击查看活动详情

起因

因为工作需要,我需要和我的学员在钉钉上面分享代码。

当有人给我分享了一段代码后,我得把这段代码拷贝下来,然后在本地创建一个 HTML 文件跑起来,才能看到效果。相当麻烦。

我在想怎么样能够一键分享代码呢?

于是我花了一天时间在网站上增加了一个代码分享的功能。

长下面这样:

然后学员只需要发送给我一个链接,我就可以直接在浏览器里面打开,并且立马可以调试和预览效果。

一切似乎很顺利,但是过了几天,学员告诉我他打开这个页面的时候,编辑器加载非常久。

编辑器是使用 Monaco Editor 开发的。很久之前我也碰到过 Monaco Editor 加载非常慢的情况,原因就是 CDN 的锅,不使用 CDN 就可以了。

在 React 中怎么使用 Monaco Editor?

通常有两种方案,一是使用官方包自己折腾。monaco-editor。这种做法必须使用编译插件,比较麻烦,而且 API 也不好用,不推荐这种做法。

二是使用 monaco 官方维护的 @monaco-editor/react,用法非常简单。

为什么用到了 CDN?

nonaco-editor/react 并没有将 monaco-editor 作为 peer 依赖,也没有把 monaco-editor 打包到源代码里。它采用的做法是通过 CDN 去加载打包好的 monaco-editor。

nonaco-editor/react 提供了 loader 方法,我们可以传递一个 config 方法,来指定 monaco 对象的来源。

它有一段默认的配置,我们可以对他进行修改。

const config = {
  paths: {
    vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/min/vs',
  },
}

export default config;

这段配置就是从 jsdelivr 上面加载 CDN。

如何避免 CDN?

避免 CDN 非常简单。

把 monaco-editor 安装到本地,并且配置 loader 的 config 就可以了。

import * as monaco from "monaco-editor";
import { loader } from "@monaco-editor/react";

loader.config({ monaco });

服务端渲染?monaco 该怎么办?

以上方法在没有服务端渲染的情况下可以用。

但是 monaco-editor 不支持服务端渲染。

比如我使用 blitz 框架,它底层是 nextjs,属于服务端框架。这时也有办法,可以动态加载。

import dynamic from "next/dynamic";

const MonacoEditor = dynamic(import("react-monaco-editor"), { ssr: false });

在很早之前这样用是没问题的,我也这么用过,但是现在不行了。

Nextjs 不支持第三方包中导入 css,该怎么办?

monaco 导入了第三方 css。

但是 Nextjs 不支持第三方包中导入 css,官方文档有说明: CSS Imported by a Dependency

也有很多人提了 issues,讨论比较多的是:issues 19936,维护者针对这个 issues 开了一个 RFC: Global CSS Imports #27953。但是目前仍然没有结论。

在这个问题彻底解决之前,可以使用 next-global-css

但是必须把 nextjs 的编译工具从 SWC 切换到 webpack。

最新版本的 Nextjs 现在还有办法这么玩吗?

切换到 webpack 很简单,只需要添加一行配置就可以了。

const config = {
  webpack: (config, options) => {
    config.plugins.push(newMonacoWebpackPlugin())
    returnconfig
  }
}

由于 monaco 必须使用 monaco-editor-webpack-plugin 这个 webpack 插件。

而这个插件必须运行在 webpack 4 版本中。原来有个 webpack5 配置项,把它设置为 false 就可以使用。

但是最新版 nextjs 不支持 webpack4 了,只支持 webpack5。

所以这个方法也不能用了。

怎么样把 jsdelivr 上面的代码下载下来?

于是我又想到,能不能把 CDN 代码下载到本地来用呢?

MonacoEditor 在 jsdelivr 上的地址:cdn.jsdelivr.net/npm/monaco-…

可是我发现它没有办法一键下载。恰巧 Monaco 的文件实在太多了,上百个,我逐一复制不知道要到什么时候。

于是我又准备搞一个工具,输入一个 url,可以一键下载该目录下所有的文件。

思路也很简单,通过 url 获取 DOM,解析 DOM,如果是文件夹则递归执行。正在我写了一半的时候,突然想到 unpkg 上面不也有 MonacoEditor 编译后的代码吗?

我又找到了 unpkg 上面 MonacoEditor 的代码地址:unpkg.com/browse/mona…

但是 unpkg 也没有一键下载功能,我想肯定有人想过如何一键下载 unpkg 上面的文件吧?

果不其然,我在 github 上面找到了一个库:github.com/TMaize/go-u…

当我把它下载下来,但是运行不起来的时候......

聪明反被聪明误

正当我准备回去继续写 jsdelivr 一键下载工具的时候,突然想到,我只要把 monoco editor 装到本地,node_module 里面不就有源代码了吗?

于是,我花了大概 1 分钟,搞定了。

现在 monaco editor 基本可以做到秒加载。

从一个灵感,到实现 MVP 版本非常快,但是在 MVP 版本增强用户体验的时候,就开始变复杂了。一个很简单的优化,却花了半天时间去折腾,最后发现走弯路了,正确的解法只需要 1 分钟而已。

其实我从一开始就应该想到的,只是因为自己对技术过于自信,导致错过了最佳方式,反而绕进了一个无底洞。

从这一点也可以看出,思考远比行动重要的多。