如何将 Monaco 编辑器添加到 Next.js 应用

2025-06-11

如何将 Monaco 编辑器添加到 Next.js 应用

底线在前

我使用了此 GitHub 评论中提到的步骤的稍微修改版本。由于我在 Next.js 中使用了 TailwindCSS,因此需要进行修改。

动机

Monaco Editor 是VS Code中使用的开源编辑器,而 VS Code 本身也是开源的。我以前经常用 VS Code 写博文,现在我搭建了自己的 Dev.to CMS,希望能拥有 Monaco 所有熟悉的功能,以便在写作时助我一臂之力。

问题

然而,我们必须处理一些问题:

  • Monaco 与框架无关,因此需要编写一些 React 绑定。
  • Monaco 是为桌面 Electron 应用程序编写的,而不是为服务器端渲染的 Web 应用程序编写的。
    • 通过使用import dynamic from "next/dynamic"Monaco 并将其作为动态导入解决了这个问题。
  • Monaco 还希望将语法高亮功能卸载到 Web Worker 中,我们需要解决这个问题
  • Next.js不希望任何依赖项从 中导入 CSS node_modules,因为这假设了捆绑器和加载器设置(例如 webpack)并且可能产生意外的全局 CSS 副作用(所有全局 CSS 都应该在 中_app.js)。

我们可以使用 GitHub 上 Elliot Hesp 提出的解决方案Next.js 团队的 Joe Haddad 提供的配置来解决这个问题。

解决方案

我使用的解决方案是根据我对 Tailwind CSS 的使用而得出的,它需要最新版本的 PostCSS,而 PostCSS@zeit/next-css只有 3.0(因为它已被弃用并且不再维护)。

我还使用 TypeScript,它引入了一个小问题,因为 Monaco EditorMonacoEnvironment在对象上附加了一个全局变量window- 我只是@ts-ignore这样做。

// next.config.js

const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
const withTM = require("next-transpile-modules")([
  // `monaco-editor` isn't published to npm correctly: it includes both CSS
  // imports and non-Node friendly syntax, so it needs to be compiled.
  "monaco-editor"
]);

module.exports = withTM({
  webpack: config => {
    const rule = config.module.rules
      .find(rule => rule.oneOf)
      .oneOf.find(
        r =>
          // Find the global CSS loader
          r.issuer && r.issuer.include && r.issuer.include.includes("_app")
      );
    if (rule) {
      rule.issuer.include = [
        rule.issuer.include,
        // Allow `monaco-editor` to import global CSS:
        /[\\/]node_modules[\\/]monaco-editor[\\/]/
      ];
    }

    config.plugins.push(
      new MonacoWebpackPlugin({
        languages: [
          "json",
          "markdown",
          "css",
          "typescript",
          "javascript",
          "html",
          "graphql",
          "python",
          "scss",
          "yaml"
        ],
        filename: "static/[name].worker.js"
      })
    );
    return config;
  }
});
Enter fullscreen mode Exit fullscreen mode

然后在你的 Next.js 应用程序代码中:

import React from "react";
// etc

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

function App() {
  const [postBody, setPostBody] = React.useState("");
  // etc
  return (<div>
  {/* etc */}
    <MonacoEditor
      editorDidMount={() => {
        // @ts-ignore
        window.MonacoEnvironment.getWorkerUrl = (
          _moduleId: string,
          label: string
        ) => {
          if (label === "json")
            return "_next/static/json.worker.js";
          if (label === "css")
            return "_next/static/css.worker.js";
          if (label === "html")
            return "_next/static/html.worker.js";
          if (
            label === "typescript" ||
            label === "javascript"
          )
            return "_next/static/ts.worker.js";
          return "_next/static/editor.worker.js";
        };
      }}
      width="800"
      height="600"
      language="markdown"
      theme="vs-dark"
      value={postBody}
      options={{
        minimap: {
          enabled: false
        }
      }}
      onChange={setPostBody}
    />
  </div>)
}
Enter fullscreen mode Exit fullscreen mode

由于我使用了 Tailwind,所以我也使用了 PostCSS,它也会尝试消除 Monaco 的 CSS。你必须告诉它忽略它:

// postcss.config.js
const purgecss = [
  "@fullhuman/postcss-purgecss",
  {
    // https://purgecss.com/configuration.html#options
    content: ["./components/**/*.tsx", "./pages/**/*.tsx"],
    css: [],
    whitelistPatternsChildren: [/monaco-editor/], // so it handles .monaco-editor .foo .bar
    defaultExtractor: content => content.match(/[\w-/.:]+(?<!:)/g) || []
  }
];
Enter fullscreen mode Exit fullscreen mode

关注 Dev.to CMS LiveStream!

鏂囩珷鏉ユ簮锛�https://dev.to/swyx/how-to-add-monaco-editor-to-a-next-js-app-ha3
PREV
如何在进行负载测试时减少三倍的代码行数
NEXT
使用 React Context、Hooks 和 Suspense 在 5 分钟内将 Netlify Identity Authentication 添加到任何 React App