交互式md文档渲染实现

🧑‍💻 写在开头

点赞 + 收藏 === 学会 🤣🤣🤣

本篇是本人在做一个交互式文章博客的一种尝试,我希望能直接将自己编写的组件渲染到md文档中,并且能够交互,类似组件库文档的渲染,简单实现了一个demo,可以将已经注册的组件通过下拉选框的方式便捷插入文档中。

🥑 你能学到什么?

希望你在阅读本文后不会觉得浪费了时间。如果你跟着学习,你将会掌握:

  • 如何在md中注入组件且可交互
  • 如何实现一个灵活的组件注册系统通过 ComponentRegistry 管理组件
  • 使用 react-markdown实现Markdown解析自定义渲染逻辑
  • 如何将静态内容转变为动态交互式内容

🍐 效果

在线体验

🍒 实现思路

组件注册表

  • 提供一个中央注册中心,管理所有可用的交互式组件
  • 确保组件注册的唯一性和全局可访问性
  • 方便在 Markdown 渲染时快速查找和使用已注册的组件
  • 在应用启动时注册默认组件
ts 复制代码
import { ComponentRegistry } from '../types/components';

class ComponentRegistryService {
  private static instance: ComponentRegistryService;
  private registry: ComponentRegistry = {};

  private constructor() {}

  public static getInstance(): ComponentRegistryService {
    if (!ComponentRegistryService.instance) {
      ComponentRegistryService.instance = new ComponentRegistryService();
    }
    return ComponentRegistryService.instance;
  }

  public register(id: string, config: ComponentRegistry[string]): void {
    this.registry[id] = config;
  }

  public get(id: string): ComponentRegistry[string] | undefined {
    return this.registry[id];
  }

  public getAll(): ComponentRegistry {
    return { ...this.registry };
  }
}

export const componentRegistry = ComponentRegistryService.getInstance();

全局注册组件

组件注册 : 通过registerComponents函数,将这些组件都注册到组件注册表中。每个组件注册时包含以下信息:

  • type: 组件类型名称
  • component: 组件本身
  • props: 组件的默认属性

main.tsx中注册全部组件

js 复制代码
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import { registerComponents } from './services/registerComponents'

// 注册所有交互式组件
registerComponents();

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

实现Markdown解析自定义渲染逻辑

  • 使用react-markdown作为基础的Markdown渲染器
  • 使用rehype-raw插件来支持原始HTML的渲染
  • 引入componentRegistry来管理交互式组件

自定义渲染规则 : 通过customComponents定义了特殊的div渲染规则:

  • 当遇到带有data-component属性的div时:

    • 获取data-component的值作为组件ID
    • 调用renderComponent渲染对应的交互式组件
    • 将渲染好的组件包装在div中返回
  • 对于普通的div,保持原样渲染

这样的设计让我们可以在Markdown中嵌入交互式组件,例如:

ini 复制代码
文本

<div data-component="counter1"></div>

文本

<div data-component="button1"></div>

当渲染这样的Markdown时,counter1button1会被替换成我们之registerComponents.ts中注册的实际交互式组件,核心代码如下

tsx 复制代码
import ReactMarkdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';
import { MarkdownComponentProps } from '../types/components';
import type { Components } from 'react-markdown';
import type { HTMLAttributes, DetailedHTMLProps } from 'react';
import { componentRegistry } from '../services/ComponentRegistry';


export const MarkdownRenderer = ({ content }: MarkdownComponentProps) => {
  const renderComponent = (id: string) => {
    const componentConfig = componentRegistry.get(id);
    if (!componentConfig) {
      console.warn(`Component with id ${id} not found in registry`);
      return null;
    }
    const componentFunction = componentConfig.component;
    const props = componentConfig.props;
    if (!componentFunction) {
      console.warn(`Component type ${componentConfig.type} not found in componentMap`);
      return null;
    }

    return componentFunction(props);
  };

  const customComponents: Components = {
    div: (props: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> & { 'data-component'?: string }) => {
      const { 'data-component': dataComponent, ...rest } = props;
      console.log('Rendering div with props:', props);
      
      if (dataComponent) {
        const component = renderComponent(dataComponent);
        if (component) {
          return <div {...rest}>{component}</div>;
        }
      }
      
      return <div {...rest} />;
    }
  };

  return (
    <div className="markdown-content">
      <ReactMarkdown
        rehypePlugins={[rehypeRaw]}
        components={customComponents}
      >
        {content}
      </ReactMarkdown>
    </div>
  );
};

🍎后序

后面我们可以引入一些开源md编辑器并集成到博客网站上去

相关推荐
yinuo44 分钟前
前端跨页面通信终极指南:方案拆解、对比分析
前端
yinuo1 小时前
前端跨页面通讯终极指南⑨:IndexedDB 用法全解析
前端
xkxnq2 小时前
第二阶段:Vue 组件化开发(第 16天)
前端·javascript·vue.js
烛阴2 小时前
拒绝配置地狱!5 分钟搭建 Three.js + Parcel 完美开发环境
前端·webgl·three.js
xkxnq2 小时前
第一阶段:Vue 基础入门(第 15天)
前端·javascript·vue.js
草梅友仁3 小时前
墨梅博客 1.0.0 发布与更新 | 2026 年第 2 周草梅周报
github·ai编程·nuxt.js
anyup3 小时前
2026第一站:分享我在高德大赛现场学到的技术、产品与心得
前端·架构·harmonyos
BBBBBAAAAAi4 小时前
Claude Code安装记录
开发语言·前端·javascript
xiaolyuh1234 小时前
【XXL-JOB】 GLUE模式 底层实现原理
java·开发语言·前端·python·xxl-job
源码获取_wx:Fegn08954 小时前
基于 vue智慧养老院系统
开发语言·前端·javascript·vue.js·spring boot·后端·课程设计