SVeditor 文档
集成方式

React / Next.js 集成

在 React 与 Next.js App Router 中挂载 SVeditor,稳定 options、自动保存与 SSR 安全边界。

React / Next.js 集成

三条规则:只在客户端创建保持稳定的 options 引用卸载时 destroy()

前置:ESM 包已安装。示例基于 Next.js App Router;其他 React 框架可忽略路由相关说明。

SSR 与 ESM 包

ESM 构建可能在 import 阶段访问浏览器全局对象。

  1. 从 client 模块 import "@wztlink1013/sveditor/style.css"
  2. 仅在浏览器加载 @wztlink1013/sveditornext/dynamic + ssr: false,或在 useEffect 中动态 import()

详见 ESM 集成 — Next.js

可复用的挂载组件

将下列模式复制到你的项目(例如 components/sveditor-mount.tsx):

"use client";

import { useEffect, useRef } from "react";
import { SVeditor } from "@wztlink1013/sveditor";
import type { EditorOptions } from "@wztlink1013/sveditor";
import { cn } from "@/lib/utils"; // 你的 className 工具
import "@wztlink1013/sveditor/style.css";

export type SveditorMountProps = {
  className?: string;
  options: Omit<EditorOptions, "el">;
};

export function SveditorMount({ className, options }: SveditorMountProps) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    const editor = SVeditor.create({ el, ...options });
    return () => editor.destroy();
  }, [options]);

  return (
    <div ref={containerRef} className={cn("min-h-[640px] w-full", className)} />
  );
}

App Router 中可通过 next/dynamic(..., { ssr: false }) 从页面 client 包装层导出。

用 useMemo 稳定 options

每次 render 新建 options 会重建编辑器,导致丢焦点或重复 onFetch

"use client";

import { useMemo } from "react";
import type { EditorOptions } from "@wztlink1013/sveditor";
import { SveditorMount } from "@/components/sveditor-mount";

type EditorInitOptions = Omit<EditorOptions, "el">;

export function DocEditor({ docId }: { docId: string }) {
  const options = useMemo<EditorInitOptions>(
    () => ({
      extensionsOptions: {
        autoSave: {
          onFetch: async () => {
            const res = await fetch(`/api/docs/${docId}`, { cache: "no-store" });
            const data = await res.json();
            return { content: data.content ?? "" };
          },
          onSaveContent: async (json) => {
            await fetch(`/api/docs/${docId}`, {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({ content: json }),
            });
          },
          debounceMs: 1000,
        },
      },
    }),
    [docId],
  );

  return <SveditorMount options={options} />;
}

自动保存 + 元数据:自动保存指南

Server Component 页面

页面保持 Server Component,向 client 子组件传参即可:

import { DocEditor } from "@/components/doc-editor";

export default async function DocPage({
  params,
}: {
  params: Promise<{ docId: string }>;
}) {
  const { docId } = await params;
  return <DocEditor docId={docId} />;
}

懒加载(可选)

import dynamic from "next/dynamic";

const DocEditor = dynamic(
  () => import("@/components/doc-editor").then((m) => m.DocEditor),
  { ssr: false, loading: () => <p>Loading editor...</p> },
);

常见模式

自动保存 + 元数据

extensionsOptions: {
  autoSave: {
    onFetch: () => fetchContent(docId),
    onSaveContent: (json) => saveContent(docId, json),
  },
  meta: {
    onFetchMeta: () => fetchMeta(docId),
    onSaveMeta: (meta) => saveMeta(docId, meta),
  },
}

不要从 autoSave.onFetch 返回 meta

自定义通知

extensionsOptions: {
  notifications: {
    toast: myToastAdapter,
    builtinToaster: false, // 仅当全局未挂载 Sonner 时设为 true
  },
}

检查清单

做法原因
挂载组件加 "use client"SDK 仅浏览器运行
useMemo([docId, ...])文档 id 变化时重建
全局或挂载模块导入 style.css布局依赖样式
effect cleanup 中 destroy()避免泄漏

注意事项

  1. 不要在 Server Component 中调用 SVeditor.create()
  2. docId 变化时加入 useMemo 依赖。
  3. 在线完整示例见 Playground

下一步