115 lines
3.3 KiB
Markdown
115 lines
3.3 KiB
Markdown
# Milkdown 自定义节点上下文经验
|
||
|
||
更新日期:2026-04-30
|
||
|
||
## 背景
|
||
|
||
知识库正文区域打开后白屏,控制台报错:
|
||
|
||
```text
|
||
MilkdownError: Context "nodes" not found, do you forget to inject it?
|
||
```
|
||
|
||
调用栈指向 `KnowledgeBaseContent` 打开知识库工具后的正文渲染链路。实际故障点不在知识库数据加载,而在 `MarkdownEditor` 挂载 Crepe/Milkdown 时注册自定义节点插件。
|
||
|
||
## 根因
|
||
|
||
Milkdown 的 `$nodeSchema()` 返回的是一组插件:
|
||
|
||
- `schema.ctx`:注入节点 schema factory 所需的上下文
|
||
- `schema.node`:读取 `schema.ctx`,再把节点写入 Milkdown 的 `nodesCtx`
|
||
|
||
因此如果手动拆开注册,顺序必须是:
|
||
|
||
```ts
|
||
schema.ctx,
|
||
schema.node,
|
||
view,
|
||
```
|
||
|
||
本次两个自定义插件曾写成 `node` 在 `ctx` 前:
|
||
|
||
- `markdownImageBlockSchema.node`
|
||
- `assetEmbedSchema.node`
|
||
|
||
当 Milkdown 初始化 schema 时,`node` 插件会先尝试读取尚未注入的 ctx,导致初始化链路异常;知识库正文默认开启 `enableAssetEmbeds`,因此会稳定走到这条路径。
|
||
|
||
## 修复思路
|
||
|
||
最小修复只调整插件注册顺序,不扩大重构:
|
||
|
||
```ts
|
||
export const markdownImageBlockPlugins = [
|
||
markdownImageBlockSchema.ctx,
|
||
markdownImageBlockSchema.node,
|
||
markdownImageBlockView,
|
||
].flat();
|
||
|
||
export const assetEmbedPlugins = [
|
||
remarkAssetEmbed,
|
||
assetEmbedSchema.ctx,
|
||
assetEmbedSchema.node,
|
||
assetEmbedView,
|
||
].flat();
|
||
```
|
||
|
||
这样保持了现有 Markdown 图片和素材嵌入能力,不改变知识库编辑器的外部 API。
|
||
|
||
## 验证
|
||
|
||
本次做了三类轻量验证:
|
||
|
||
```bash
|
||
pnpm nx run drawnix:typecheck
|
||
```
|
||
|
||
- 类型检查通过
|
||
- 用 JSDOM 直接创建 Crepe + 自定义 `$nodeSchema` 插件,确认 `ctx -> node -> view` 顺序可正常初始化
|
||
- 在本地 `localhost:7200` 打开知识库,控制台未再出现 `Context "nodes"` 错误
|
||
|
||
`.npmrc` 中缺少 `NPM_TOKEN` 只会产生 pnpm warning,不影响类型检查结论。
|
||
|
||
## 经验
|
||
|
||
### 1. 不要随手拆散组合插件
|
||
|
||
Milkdown 的 `$nodeSchema()`、`$remark()` 这类 helper 通常返回带依赖顺序的插件组合。能整体 `.use(schema)` 时优先整体使用;必须拆开时,要确认每个子插件的上下文依赖。
|
||
|
||
### 2. 自定义节点注册顺序要固定
|
||
|
||
推荐约定:
|
||
|
||
```ts
|
||
remarkPlugin,
|
||
nodeSchema.ctx,
|
||
nodeSchema.node,
|
||
nodeView,
|
||
inputRules,
|
||
commands,
|
||
```
|
||
|
||
其中 `ctx` 永远在读取它的 `node`、`view`、`command` 之前。
|
||
|
||
### 3. 编辑器白屏优先查插件链
|
||
|
||
遇到 Milkdown/Crepe 白屏时,排查顺序:
|
||
|
||
1. 是否重复注册同名 node/mark
|
||
2. `$nodeSchema.ctx` 是否在 `$nodeSchema.node` 前
|
||
3. `view` 是否等待 schema ready 后才读取 node type
|
||
4. 是否存在多份 `@milkdown/core` 或 `@milkdown/ctx`
|
||
5. 是否在 editor created 前调用 `editorViewCtx`
|
||
|
||
### 4. 同名扩展要单独立项
|
||
|
||
当前自定义 `image-block` 与 Crepe 内置 `ImageBlock` 存在同名扩展关系。最小修复只处理初始化顺序;后续若继续治理,建议单独评估:
|
||
|
||
- 扩展内置 `imageBlockSchema`
|
||
- 或关闭 `CrepeFeature.ImageBlock` 后由自定义插件完整接管
|
||
|
||
这类变更会影响图片上传、caption、resize、素材库插图等体验,不应混入白屏热修。
|
||
|
||
## 一句话结论
|
||
|
||
Milkdown 自定义节点不是普通数组拼接,`ctx` 是 schema 初始化的地基;先注入上下文,再注册节点,最后挂 view。✅
|