如何将GrapesJS集成到Remix应用中(2026年完整指南)

将GrapesJS嵌入Remix应用:在客户端安装在useEffect中,将内容保存到Remix动作中,导出干净的HTML/CSS。

DevFuture Development
DevFuture Development
2026年5月21日1 个月前
阅读约 5 分钟7 次浏览

为什么GrapesJS适合Remix

GrapesJS 需要 DOM,所以在 Remix 应用里你会在里面初始化它 useEffect (仅客户端)而 Remix 的加载器和动作处理数据 在服务器上。本指南安装编辑器,通过Remix动作保存,并且 导出HTML/CSS。

1. 挂载编辑器客户端

创造 app/routes/editor.tsx。在效果中导入GrapesJS,所以它 SSR期间从不运行。

import { useEffect, useRef } from 'react';
import 'grapesjs/dist/css/grapes.min.css';

export default function Editor() {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    let editor: any;
    (async () => {
      const grapesjs = (await import('grapesjs')).default;
      editor = grapesjs.init({
        container: ref.current!,
        height: '100vh',
        fromElement: false,
        storageManager: false,
        components: '<h1>Hello from GrapesJS</h1>',
      });

      // Persist via a Remix action.
      document.getElementById('save')!.onclick = async () => {
        await fetch('/editor', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            html: editor.getHtml(),
            css: editor.getCss(),
            project: editor.getProjectData(),
          }),
        });
      };
    })();
    return () => editor?.destroy();
  }, []);

  return (
    <>
      <button id="save">Save</button>
      <div ref={ref} />
    </>
  );
}

2. 在混音操作中处理存档

// same app/routes/editor.tsx
import type { ActionFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';
import { savePage } from '~/models/page.server';

export async function action({ request }: ActionFunctionArgs) {
  const data = await request.json();
  await savePage('home', data);   // your DB write
  return json({ status: 'ok' });
}

操作运行在服务器端,所以你的数据库客户端不会进入浏览器 捆绑。

3. 返回保存的内容

export async function loader() {
  return json(await getPage('home'));
}
// in the effect, after init:
// const saved = await fetch('/editor?_data=...'); editor.loadProjectData(saved.project);

混音版常见的陷阱

Code on a screen in a dark editor
仅初始化编辑器为 useEffect;让DB继续参与战斗。

Remix的服务器/客户端分离出了问题。在模块顶层导入 GrapesJS 时,在 SSR 期间运行它,导致路由崩溃——而是动态导入到内部 useEffect 。保持 action 所有持久化在路由(服务器)中,切勿将数据库客户端导入组件,否则会泄漏到浏览器捆绑包中。如果你加载了保存的项目,先在 init 后读取并 loader 调用 editor.loadProjectData() ——不要在 SSR 期间尝试渲染编辑器状态。最后,从效果中返回 editor.destroy() ,这样客户端的过渡不会堆叠编辑器实例。

前提条件

你需要Node.js 18+ 和一个 Remix 2 应用。没有专门针对 Remix 的 GrapesJS 包 必须——编辑器仅支持浏览器,Remix的加载器/操作处理数据 服务器。熟悉路由, useEffect以及混音动作 够了。

向编辑器添加自定义方块

init 后(效果内)用块管理器注册可拖拽的块:

editor.BlockManager.add('hero', {
  label: 'Hero section',
  category: 'Sections',
  content: '<section class="hero"><h1>Headline</h1><p>Copy</p></section>',
});

从GJS拉取现成的块库和预设。市场要买更丰富的套装。

存储深度潜航:动作 + 加载器

保持服务器的持久性。将项目发布到该路线 action 然后从中 loader读取,这样你的数据库客户端就不会泄露 进入浏览器捆绑包:

export async function action({ request }) {
  const data = await request.json();
  await savePage('home', data);          // server-only DB write
  return json({ status: 'ok' });
}
export async function loader() {
  return json(await getPage('home'));    // returns the saved project
}

init 后,呼叫 editor.loadProjectData(saved.project) 重新打开页面。

表演技巧

动态导入GrapesJS useEffect ,避免它进入 主捆绑包和服务器渲染路径,以及返回 editor.destroy() 所以客户端切换不会叠加实例。代码分段繁重插件 就是使用这些元素的功能。

安全考量

在写作前确认并授权该行为——绝不要接受 未经认证的帖子覆盖页面。如果 非管理员可以编辑。验证有效载荷大小,这样大型项目就不会耗尽 记忆。

排查常见问题

“窗口/文档未定义” 意味着GrapesJS在SSR期间运行—— 导入并只在 useEffect内部启动。 一个未被打扮的 Canvas 意味着样式表导入缺失。 一片空白 编辑器 意味着容器引用在初始化时还没准备好。补水警告 通常意味着你在SSR期间尝试渲染编辑器状态。

什么时候用GrapesJS搭配Remix

当你的 Remix 应用嵌入真实的可视化页面或邮件构建器时,GrapesJS 就很适合你的 用户控制,拥有自己的存储和HTML输出。对于内嵌富文本,a 更轻的编辑器就足够了;用于全页构图,包含版面、样式和 干净导出,GrapesJS 是更强大的、获得麻省理工学院许可的自托管选择。

下一步

参见相关的 GrapesJS + ReactGrapesJS + Next.js 指南,请浏览 GrapesJS 市场,或者从那里开始 GJS。市场主页

常见问题

GrapesJS 能和 Remix 服务器渲染一起使用吗?

是的——只需 useEffect 在浏览器中初始化,确保它能在浏览器中运行。该 路由由服务器渲染;只有编辑器实例是仅客户端的。

我如何在Remix中保存GrapesJS的数据?

把项目数据发布到Remix操作中,并持久保存到你的数据库中。行动 在服务器上运行。

我该如何重新加载保存的内容?

从加载器获取已保存的项目并调用 editor.loadProjectData(saved) 之后 grapesjs.init

更多标签:
发布于 2026年5月21日
更新于 2026年6月27日
🔌 GJS.Market

在寻找 GrapesJS 插件吗?

超过 100 款精选插件、预设与模板 —— 由社区精挑细选并持续维护。

分享本文TwitterFacebookLinkedIn
发布平台
DevFuture Development
DevFuture Development
访问店铺 →

更多来自 DevFuture Development

发现更多精彩文章,及时获取最新内容。

查看全部文章

来自 DevFuture Development 的付费插件

由该作者精心打造的精选付费插件。

访问店铺 →