发布于 2025-12-10 11 阅读
0

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

如何将 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