核心思路是将样式控制权交给使用者,通过具名插槽暴露语义化节点与上下文数据,如heading、blockquote、thematic-break等,并用作用域插槽传递结构化数据;默认插槽兜底并配合scoped样式隔离;轻量样式用css变量,重度定制才用插槽,命名须语义清晰一致。

核心思路是把样式控制权交给使用者,而不是在组件内部硬编码 CSS 类或写死结构。插槽不是为了“替换渲染逻辑”,而是暴露语义化节点与上下文数据,让父组件能按需定制外观和行为。
用具名插槽接管特定块级元素
标题、引用、代码块、分隔线这些有明确语义的块,应各自对应一个具名插槽。解析器识别后不直接生成 HTML,而是触发对应插槽,并传入原始内容和元信息。
-
<slot name="heading" :level="2" :text="node.text"></slot>—— 支持统一处理所有标题层级,也方便加锚点或图标 -
<slot name="blockquote" :cite="node.cite"></slot>—— 透传引用来源,可用于右上角显示出处 -
<slot name="thematic-break"></slot>—— 命名为 thematic-break 比hr更准确,因 Markdown 中***或---并非等价于语义化的<hr>
通过作用域插槽传递可操作的结构化数据
仅传文本不够,样式常依赖上下文判断。比如是否加边框、是否启用懒加载、是否添加交互提示,都得靠插槽暴露的数据驱动。
将 Markdown 格式的回复转换为适合 Telegram 的富文本,避免渲染错误。适用于用户偏好 Markdown 但目标平台为 Telegram 的场景。
-
<slot name="code-block" :lang="node.lang" :highlighted="htmlString" :fence="node.fence"></slot>—— 高亮结果由组件内部完成并透传,父组件只负责包裹和样式 -
<slot name="table" :headers="node.headers" :rows="node.rows" :is-striped="true"></slot>—— 表格斑马纹、响应式折叠等逻辑交由使用者决定 -
<slot name="image" :src="node.src" :alt="node.alt" :title="node.title" :loading="'lazy'"></slot>—— 可绑定class控制圆角/阴影,也可动态加data-src实现渐进加载
默认插槽兜底 + 样式隔离策略
对未被具名插槽覆盖的通用内容(如段落、强调、无序列表),统一走默认插槽,并配合 scoped 样式避免污染全局。
- 组件根节点加 class="markdown-body",所有默认内容包裹其中
- 样式中写
.markdown-body p { margin: 1.5em 0; },不依赖重置库 - 若需深度定制某类块(如修改
<blockquote></blockquote>的左侧色条宽度),仍建议提供blockquote插槽,而非开放 CSS 变量——变量难以精准作用于嵌套子元素
避开常见设计陷阱
不是所有样式需求都适合用插槽解决。轻量视觉调整优先走 CSS 自定义属性;重度结构改造才动用插槽。
- 纯颜色、字体大小、行高等微调,用
--md-text-color+:deep()更简洁 - 切勿在插槽内部重复解析 Markdown(例如在
code-block插槽里再调 highlight.js),渲染逻辑必须收敛在组件内部 - 插槽命名要语义清晰、保持一致性,如用
thematic-break而非divider,用task-list-item而非checkbox









