# 高级解析与低层自定义 这页面向更底层的解析定制:token 变换、AST 后处理,以及自定义节点管线。 如果你只是要完成这些常见任务,建议先去更直接的页面: - 替换 `image`、`code_block`、`mermaid` 这类内置渲染器: [覆盖内置组件](/zh/guide/component-overrides) - 支持 `thinking` 这类可信标签: [自定义标签与高级组件](/zh/guide/custom-components) - 只想调现有行为,不想改解析器: [Props 与选项](/zh/guide/props) ## parseOptions `parseOptions` 可传递给 `MarkdownRender` 或直接用于 `parseMarkdownToStructure`。 - `preTransformTokens?: (tokens: MarkdownToken[]) => MarkdownToken[]` — 在 `markdown-it` 解析后立即变换 tokens - `postTransformTokens?: (tokens: MarkdownToken[]) => MarkdownToken[]` — 进一步变换 tokens 如需改造 AST,可在 `parseMarkdownToStructure` 返回后自行处理 `ParsedNode[]`,再通过 `MarkdownRender` 的 `nodes` 传入。 ### 示例:自定义 HTML‑like 标签(推荐) 对于 `...` 这类简单自定义标签,现在不再需要先正则换行或重写 token。只要把标签加入白名单并注册组件即可: ```ts import { setCustomComponents } from 'markstream-vue' import ThinkingNode from './ThinkingNode.vue' setCustomComponents('docs', { thinking: ThinkingNode }) ``` ```vue ``` 当 `custom-html-tags` 包含某个标签名时,解析器会: - 在流式场景下吞并未闭合中间态,直到 `` 完整出现; - 输出 `CustomComponentNode`(`type: 'thinking'`,即标签名本身,不含尖括号),并带上 `content`、可选 `attrs` 与 `loading/autoClosed` 标记。 ## 自定义组件解析示例 上面的内置白名单 + 自定义节点管线已覆盖大多数“组件式”标签(内联或块级)。 当你需要进一步改造节点(例如剥掉包裹、合并分段、手动映射 attrs)时,再使用钩子: ```ts import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue' function preTransformTokens(tokens: MarkdownToken[]) { return tokens.map((t) => { if (t.type === 'html_block' && t.tag === 'thinking') return { ...t, content: String(t.content ?? '').replace(/<\/?thinking[^>]*>/g, '').trim() } return t }) } const md = getMarkdown() const nodes = parseMarkdownToStructure(markdown, md, { preTransformTokens }) ``` 其他可选方案(按复杂度递增): - 后端将 `thinking` 拆成单独字段/类型:前端用两次 `MarkdownRender`,一个渲染 `thinking`,另一个渲染剩余正文,互不干扰。 - 在进入解析前用正则占位:先 `replace` 掉 `...` 保存到数组;正文用 `MarkdownRender`;最后把占位符替换回自定义组件渲染结果。`thinking` 常在开头时可以直接截取头部做单独渲染。 ## setCustomComponents(id, mapping) - 使用 `setCustomComponents('docs', { thinking: ThinkingComponent })` 作用于带 `custom-id="docs"` 的 `MarkdownRender` 实例。 - 调用 `removeCustomComponents` 清理映射,避免单页应用内存泄漏。 ## 作用域示例 ```vue // 在 setup 中 setCustomComponents('docs', { thinking: ThinkingNode }) ``` 高级钩子是为 Markdown 添加领域语法的强大方式,无需更改核心解析器。 ### Typewriter 属性 `MarkdownRender` 支持 `typewriter` 布尔属性,控制非 `code_block` 节点的轻量 fade 表现。新节点仍然会走小型 enter 过渡,而流式过程中原地追加出来的那段新文本也会单独补一段短 fade,不再让整个节点容器一起变暗。适用于演示 UI,但在 SSR 或打印/导出场景下可能不需要。 示例: ```vue ``` CSS 变量:`--typewriter-fade-duration` 和 `--typewriter-fade-ease` 用于首屏 enter 过渡;`--stream-update-fade-duration` 和 `--stream-update-fade-ease` 用于流式追加文本时那段短 fade。若不单独覆盖,流式追加的 fade 会默认沿用 `typewriter` 的时长和缓动。 ## 国际化(i18n) 默认 `getMarkdown` 使用英文 UI 文案(如代码块复制按钮)。你可以通过 `i18n` 选项自定义这些文本: **翻译映射用法:** ```ts import { getMarkdown } from 'markstream-vue' const md = getMarkdown('editor-1', { i18n: { 'common.copy': '复制', } }) ``` **翻译函数用法:** ```ts import { getMarkdown } from 'markstream-vue' import { useI18n } from 'vue-i18n' // 或其他 i18n 库 const { t } = useI18n() const md = getMarkdown('editor-1', { i18n: (key: string) => t(key) }) ``` **全局覆盖默认文案:** 如果你只是想修改一些默认 UI 文案(例如 "Copy" → "复制"),而不需要完整的 i18n 功能,可以使用 `setDefaultI18nMap` 来替换默认的翻译映射: ```ts import { setDefaultI18nMap } from 'markstream-vue' setDefaultI18nMap({ 'common.copy': '复制', 'common.copied': '已复制', 'common.decrease': '减少', 'common.reset': '重置', 'common.increase': '增加', 'common.expand': '展开', 'common.collapse': '收起', 'common.preview': '预览', 'common.source': '源码', 'common.export': '导出', 'common.open': '打开', 'common.zoomIn': '放大', 'common.zoomOut': '缩小', 'common.resetZoom': '重置缩放', 'image.loadError': '图片加载失败', 'image.loading': '图片加载中...', }) ``` 这个功能适合以下场景: - 不需要语言切换,只是觉得默认文案不合适 - 希望默认使用中文(或其他语言)UI - 没有使用 `vue-i18n`,但仍想自定义 UI 文案 **默认翻译键:** - `common.copy`:代码块复制按钮文本 - `common.copied`:复制成功后的提示文本 - `image.loadError`:图片加载失败的提示 - `image.loading`:图片加载中的提示 该设计保证 markdown 工具函数纯净,无全局副作用,可与任意 i18n 方案集成或直接传入静态翻译。