如何将 Monaco 编辑器添加到 Next.js 应用
底线在前
我使用了此 GitHub 评论中提到的步骤的稍微修改版本。由于我在 Next.js 中使用了 TailwindCSS,因此需要进行修改。
- YouTube 录音(18 分钟)
- Dev.to 嵌入:
动机
Monaco Editor 是VS Code中使用的开源编辑器,而 VS Code 本身也是开源的。我以前经常用 VS Code 写博文,现在我搭建了自己的 Dev.to CMS,希望能拥有 Monaco 所有熟悉的功能,以便在写作时助我一臂之力。
问题
然而,我们必须处理一些问题:
- Monaco 与框架无关,因此需要编写一些 React 绑定。
- 您可以自己做,但也可以跳过这一步并使用https://github.com/react-monaco-editor/react-monaco-editor
- Monaco 是为桌面 Electron 应用程序编写的,而不是为服务器端渲染的 Web 应用程序编写的。
- 通过使用
import dynamic from "next/dynamic"
Monaco 并将其作为动态导入解决了这个问题。
- 通过使用
- Monaco 还希望将语法高亮功能卸载到 Web Worker 中,我们需要解决这个问题
- Next.js不希望任何依赖项从 中导入 CSS
node_modules
,因为这假设了捆绑器和加载器设置(例如 webpack)并且可能产生意外的全局 CSS 副作用(所有全局 CSS 都应该在 中_app.js
)。@zeit/next-css
我们可以用和重新启用它next-transpile-modules
我们可以使用 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;
}
});
然后在你的 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>)
}
由于我使用了 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) || []
}
];
关注 Dev.to CMS LiveStream!
- 第 1 天 - 设置 Next.js 和 Tailwind UI,通过 API 路由列出帖子- 90 分钟
- 第 2 天 - 使用 Next.js、Tailwind UI、Highlight.js、React Hook Form 和 React Query 设置 Markdown 编辑器- 3 小时
- 快速修复 -如何将 Monaco 编辑器添加到 Next.js 应用- 18 分钟
- 第 3 天 - 重构以编辑现有帖子- 3 小时
- 第四天 - 波兰日!实现通知、Markdown 预览、程序化重定向,以及在 Next.js 中使用 Web 组件- 3 小时
- 使用 OneGraph 即时 GraphQL - 与 Sean Grove 公开屏幕共享- 使用 OneGraph 和 GraphQL 重构手动 Dev.to API 访问
- 如何以及为何取消重置 Tailwind 的 CSS 重置