Slate 中如何正确处理自定义元素的反序列化?

上官世暄 阅读 25

我在用 Slate 做一个支持代码块的富文本编辑器,序列化时没问题,但反序列化回来就报错。我试过在 deserialize 函数里加判断,但一遇到 code 类型就直接崩溃。

控制台提示 “Cannot resolve a DOM point from Slate point”,是不是我反序列化的结构写错了?下面是我目前的反序列化逻辑:

const deserialize = el => {
  if (el.nodeType === 3) {
    return el.textContent;
  } else if (el.nodeType !== 1) {
    return null;
  }

  const tagName = el.tagName.toLowerCase();
  if (tagName === 'pre') {
    return {
      type: 'code',
      children: [{ text: el.innerText }]
    };
  }

  // ...其他元素处理
}
我来解答 赞 7 收藏
二维码
手机扫码查看
2 条解答
西门新红
这个问题我之前也碰到过!报错原因很简单:Slate 的反序列化要求块级节点的 children 必须是行内节点(inline nodes),不能直接放文本节点。

你的代码里 children: [{ text: el.innerText }] 这样写是不对的,text 节点是叶子节点,不能直接作为 code 块的子节点。

改成这样就行:

const deserialize = el => {
if (el.nodeType === 3) {
return el.textContent;
} else if (el.nodeType !== 1) {
return null;
}

const tagName = el.tagName.toLowerCase();
if (tagName === 'pre') {
return {
type: 'code',
children: [{ text: '' }] // 块级节点至少要有一个子节点
};
}

// ...其他元素处理
}


等等,这样文本会丢失。你需要先把代码内容解析出来放到一个数组里,然后每个文本行都要包装成行内节点:

if (tagName === 'pre') {
const codeText = el.innerText;
const lines = codeText.split('n');

return {
type: 'code',
children: lines.map(line => ({
type: 'line',
children: [{ text: line }]
}))
};
}


这里用 line 作为行内节点类型来包裹每一行代码。如果你没有定义 line 这个类型,那就用 paragraph 代替,或者直接在 schema 里把 code 块标记为 void,这样就不需要 children 了:

// schema 里这样配置
code: {
isVoid: true,
children: [{ text: '' }]
}


总之核心问题就是:块级节点不能直接放 text 作为 children,必须包一层行内节点。试试看能不能跑通!
点赞
2026-03-20 08:00
公孙紫瑶
这个报错我之前也踩过坑,问题出在你的 children 结构上。

Slate 的 block 元素有个硬性规定:children 数组里的每一项必须包含 text 属性或者是一个有效的嵌套元素。你现在的写法看着没问题,但问题可能出在两个地方。

第一个是 innerText 会把换行符给吃掉,导致多行代码变成一行,Slate 在计算 point 位置时就会对不上。第二个是你可能把 code 类型注册成了 void 元素,但反序列化时没按 void 的规则处理。

给你一个修正版本:

const deserialize = el => {
if (el.nodeType === 3) {
return { text: el.textContent };
} else if (el.nodeType !== 1) {
return null;
}

const tagName = el.tagName.toLowerCase();

if (tagName === 'pre') {
// 用 textContent 而不是 innerText,保留换行符
const codeEl = el.querySelector('code') || el;
return {
type: 'code',
children: [{ text: codeEl.textContent || '' }],
};
}

// 其他元素处理...
}


注意我把 nodeType === 3 的返回值也改成了 { text: el.textContent },这是 Slate 的标准格式,直接返回纯字符串有时候会出问题。

如果你在 createEditor 时把 code 设成了 void 类型,那还得加个 void 标记:

if (tagName === 'pre') {
const codeEl = el.querySelector('code') || el;
return {
type: 'code',
void: true, // 如果你的 schema 里 code 是 void 类型
children: [{ text: '' }], // void 元素 children 必须是空文本
};
}


你可以先打印一下反序列化后的 JSON 结构,确认 children 里的每一层都有 text 字段,这个报错信息基本上都是结构不对导致的。
点赞 3
2026-03-01 13:00