ByteNoteByteNote

字节笔记本

2026年2月20日

Mermaid 图表绘制完全指南:从入门到精通

API中转
¥120

Mermaid 是一个基于 JavaScript 的图表绘制工具,使用类似 Markdown 的文本语法生成流程图、时序图、类图等多种图表。本文详细介绍在 VibeAny 项目中使用 Mermaid 的各种配置和最佳实践。

什么是 Mermaid

Mermaid (美人鱼) 让你用文本语法创建图表,无需复杂的绘图工具。它的核心优势:

特性说明
文本即图表使用类 Markdown 语法,版本控制友好
多种图表流程图、时序图、类图、甘特图等 10+ 种
实时渲染浏览器端动态渲染,无需后端
主题定制支持多种内置主题和自定义样式
广泛集成GitHub、GitLab、Notion 等原生支持

基础使用

安装

bash
# npm
npm install mermaid

# pnpm (推荐)
pnpm add mermaid

# yarn
yarn add mermaid

基本渲染

typescript
import mermaid from 'mermaid'

// 初始化配置
mermaid.initialize({
  startOnLoad: true,
  theme: 'default'
})

// 渲染图表
const graphDefinition = `
graph TD
    A[开始] --> B{判断}
    B -->|条件1| C[处理1]
    B -->|条件2| D[处理2]
    C --> E[结束]
    D --> E
`

// 在指定元素中渲染
mermaid.render('graph-div', graphDefinition).then(({ svg }) => {
  document.getElementById('graph-container').innerHTML = svg
})

图表类型详解

1. 流程图 (Flowchart)

最常用的图表类型,展示流程和决策:

mermaid
graph TD
    A[用户访问] --> B{是否登录}
    B -->|是| C[显示首页]
    B -->|否| D[跳转登录]
    D --> E[输入凭证]
    E --> F{验证成功}
    F -->|是| C
    F -->|否| G[显示错误]
    G --> E

语法详解

mermaid
graph TD                    %% TD=从上到下, LR=从左到右
    A[矩形节点]             %% [] 表示矩形
    B(圆角矩形)             %% () 表示圆角矩形
    C[[子程序]]             %% [[]] 表示子程序形状
    D>不对称矩形]           %% >] 表示不对称
    E{菱形判断}             %% {} 表示菱形
    F{{六边形}}             %% {{}} 表示六边形
    G[/平行四边形/]         %% [/ /] 表示平行四边形
    H[\反向平行四边形\]     %% [\ \] 表示反向
    I[/梯形\\]              %% [/\\] 表示梯形
    
    A --> B                 %% --> 实线箭头
    B -.-> C                %% -.-> 虚线箭头
    C ==> D                 %% ==> 粗线箭头
    D -->|带文字| E         %% |文字| 添加标签
    E --> 无箭头 F          %% --> 无箭头

2. 时序图 (Sequence Diagram)

展示系统间或对象间的交互顺序:

mermaid
sequenceDiagram
    participant U as 用户
    participant C as 客户端
    participant A as API网关
    participant S as 服务端
    participant D as 数据库
    
    U->>C: 点击提交
    C->>A: POST /api/order
    A->>S: 转发请求
    S->>D: 查询库存
    D-->>S: 返回库存状态
    
    alt 库存充足
        S->>D: 创建订单
        D-->>S: 订单创建成功
        S-->>A: 返回成功
        A-->>C: 200 OK
        C-->>U: 显示成功页面
    else 库存不足
        S-->>A: 返回错误
        A-->>C: 400 Bad Request
        C-->>U: 显示库存不足
    end

语法详解

mermaid
sequenceDiagram
    %% 定义参与者
    participant A as 参与者A
    participant B as 参与者B
    
    %% 消息类型
    A->>B: 实线箭头(异步)
    A-->>B: 虚线箭头(返回)
    A->xB: 实线无箭头
    A--xB: 虚线无箭头
    A->>B: 实线实心箭头(同步)
    A-->>B: 虚线实心箭头
    
    %% 激活框
    activate A
    A->>B: 请求
    activate B
    B-->>A: 响应
    deactivate B
    deactivate A
    
    %% 条件分支
    alt 条件1
        A->>B: 操作1
    else 条件2
        A->>B: 操作2
    else 默认
        A->>B: 默认操作
    end
    
    %% 循环
    loop 每分钟
        A->>B: 心跳检测
    end
    
    %% 并行
    par 并行操作1
        A->>B: 请求1
    and 并行操作2
        A->>C: 请求2
    end

3. 类图 (Class Diagram)

展示类的结构和关系:

mermaid
classDiagram
    class User {
        +String id
        +String email
        +String name
        +Date createdAt
        +login()
        +logout()
    }
    
    class Order {
        +String id
        +String userId
        +Number total
        +String status
        +placeOrder()
        +cancelOrder()
    }
    
    class Product {
        +String id
        +String name
        +Number price
        +Number stock
    }
    
    class OrderItem {
        +String orderId
        +String productId
        +Number quantity
        +Number price
    }
    
    User "1" --> "*" Order : 拥有
    Order "1" --> "*" OrderItem : 包含
    OrderItem "*" --> "1" Product : 引用
    
    note for User "用户实体类"
    note for Order "订单实体类"

关系类型

mermaid
classDiagram
    classA --|> classB : 继承 (Inheritance)
    classC --* classD : 组合 (Composition)
    classE --o classF : 聚合 (Aggregation)
    classG --> classH : 关联 (Association)
    classI .. classJ : 依赖 (Dependency)
    classK ..|> classL : 实现 (Realization)
    classM -- classN : 链接 (Link)

4. 甘特图 (Gantt)

项目进度管理:

mermaid
gantt
    title 项目开发计划
    dateFormat  YYYY-MM-DD
    section 设计阶段
    需求分析           :done, a1, 2024-01-01, 3d
    UI设计             :active, a2, after a1, 5d
    原型评审           :a3, after a2, 2d
    
    section 开发阶段
    后端API开发        :b1, after a3, 10d
    前端页面开发       :b2, after a3, 12d
    接口联调           :b3, after b1, 3d
    
    section 测试阶段
    单元测试           :c1, after b2, 5d
    集成测试           :c2, after b3, 5d
    Bug修复            :c3, after c2, 3d
    
    section 发布阶段
    部署上线           :d1, after c3, 2d
    用户培训           :d2, after d1, 3d

5. 状态图 (State Diagram)

展示状态转换:

mermaid
stateDiagram-v2
    [*] --> 待付款
    待付款 --> 已付款 : 支付成功
    待付款 --> 已取消 : 超时/取消
    已付款 --> 已发货 : 商家发货
    已付款 --> 已退款 : 申请退款
    已发货 --> 已收货 : 确认收货
    已发货 --> 已退货 : 申请退货
    已收货 --> [*]
    已取消 --> [*]
    已退款 --> [*]
    已退货 --> [*]

6. 实体关系图 (ER Diagram)

数据库设计:

mermaid
erDiagram
    USER ||--o{ ORDER : places
    USER {
        string id PK
        string email
        string password
        string name
        datetime created_at
    }
    
    ORDER ||--|{ ORDER_ITEM : contains
    ORDER {
        string id PK
        string user_id FK
        decimal total_amount
        string status
        datetime created_at
    }
    
    ORDER_ITEM }|--|| PRODUCT : references
    ORDER_ITEM {
        string id PK
        string order_id FK
        string product_id FK
        int quantity
        decimal price
    }
    
    PRODUCT {
        string id PK
        string name
        string description
        decimal price
        int stock
    }

在 React 中使用

基础组件

tsx
'use client'

import { useEffect, useRef } from 'react'
import mermaid from 'mermaid'

interface MermaidDiagramProps {
  chart: string
  className?: string
}

export function MermaidDiagram({ chart, className }: MermaidDiagramProps) {
  const containerRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    if (!containerRef.current) return
    
    mermaid.initialize({
      startOnLoad: false,
      theme: 'default',
      securityLevel: 'strict',
    })
    
    mermaid.render('mermaid-svg', chart).then(({ svg }) => {
      if (containerRef.current) {
        containerRef.current.innerHTML = svg
      }
    })
  }, [chart])

  return <div ref={containerRef} className={className} />
}

支持深色模式

tsx
'use client'

import { useEffect, useRef } from 'react'
import mermaid from 'mermaid'
import { useTheme } from 'next-themes'

interface MermaidDiagramProps {
  chart: string
}

export function MermaidDiagram({ chart }: MermaidDiagramProps) {
  const containerRef = useRef<HTMLDivElement>(null)
  const { theme } = useTheme()

  useEffect(() => {
    if (!containerRef.current) return
    
    const mermaidTheme = theme === 'dark' ? 'dark' : 'default'
    
    mermaid.initialize({
      startOnLoad: false,
      theme: mermaidTheme,
      themeVariables: {
        // 自定义变量
        primaryColor: '#e1f5fe',
        primaryTextColor: '#01579b',
        primaryBorderColor: '#0288d1',
        lineColor: '#0288d1',
        secondaryColor: '#fff3e0',
        tertiaryColor: '#e8f5e9',
      }
    })
    
    const id = `mermaid-${Math.random().toString(36).substr(2, 9)}`
    
    mermaid.render(id, chart).then(({ svg }) => {
      if (containerRef.current) {
        containerRef.current.innerHTML = svg
      }
    })
  }, [chart, theme])

  return (
    <div 
      ref={containerRef} 
      className="flex justify-center overflow-x-auto"
    />
  )
}

Markdown 中自动渲染

tsx
'use client'

import { useEffect } from 'react'
import mermaid from 'mermaid'

interface MarkdownContentProps {
  content: string
}

export function MarkdownContent({ content }: MarkdownContentProps) {
  useEffect(() => {
    mermaid.initialize({
      startOnLoad: true,
      theme: 'default',
    })
    
    // 查找并渲染所有 mermaid 代码块
    mermaid.run({
      querySelector: '.language-mermaid',
    })
  }, [content])

  return (
    <div 
      className="prose"
      dangerouslySetInnerHTML={{ __html: content }} 
    />
  )
}

主题定制

内置主题

typescript
mermaid.initialize({
  theme: 'default'    // 默认浅色
  // theme: 'dark'    // 深色主题
  // theme: 'forest'  // 森林绿
  // theme: 'neutral' // 中性灰
  // theme: 'base'    // 基础主题(可自定义)
})

自定义主题

typescript
mermaid.initialize({
  theme: 'base',
  themeVariables: {
    // 主要颜色
    primaryColor: '#bbdefb',
    primaryTextColor: '#1565c0',
    primaryBorderColor: '#1976d2',
    
    // 线条
    lineColor: '#424242',
    
    // 次要颜色
    secondaryColor: '#ffe0b2',
    secondaryTextColor: '#e65100',
    secondaryBorderColor: '#f57c00',
    
    // 第三颜色
    tertiaryColor: '#c8e6c9',
    tertiaryTextColor: '#2e7d32',
    tertiaryBorderColor: '#388e3c',
    
    // 背景
    background: '#fafafa',
    mainBkg: '#ffffff',
    
    // 节点边框
    nodeBorder: '#bdbdbd',
    clusterBkg: '#f5f5f5',
    clusterBorder: '#9e9e9e',
    
    // 标题
    titleColor: '#212121',
    edgeLabelBackground: '#ffffff',
    
    // 时序图特定
    actorBorder: '#757575',
    actorBkg: '#ffffff',
    actorTextColor: '#212121',
    actorLineColor: '#bdbdbd',
    signalColor: '#424242',
    signalTextColor: '#212121',
  }
})

CSS 样式覆盖

css
/* 自定义 Mermaid 样式 */
.mermaid {
  font-family: 'Inter', sans-serif;
}

/* 节点样式 */
.mermaid .node rect {
  fill: #e3f2fd;
  stroke: #1976d2;
  stroke-width: 2px;
  rx: 8;
  ry: 8;
}

/* 文字样式 */
.mermaid .node .label {
  color: #1565c0;
  font-size: 14px;
  font-weight: 500;
}

/* 连线样式 */
.mermaid .edgePath .path {
  stroke: #424242;
  stroke-width: 2px;
}

/* 箭头样式 */
.mermaid .arrowheadPath {
  fill: #424242;
}

Cloudflare Workers 中的 SSR Stubs

问题

Mermaid 依赖浏览器 DOM API,在服务端渲染时会报错:

Error: document is not defined

解决方案

typescript
// vite.config.ts
const ssrOnlyStubs = () => ({
  name: 'ssr-only-stubs',
  enforce: 'pre',
  resolveId(id, importer, { ssr }) {
    if (!ssr) return null
    
    // Mermaid 及其依赖
    if (id.includes('mermaid') || 
        id.includes('beautiful-mermaid') ||
        id.includes('@streamdown/')) {
      return '\0stub:mermaid'
    }
  },
  load(id) {
    if (id === '\0stub:mermaid') {
      return `
        export default {
          initialize: () => {},
          render: () => Promise.resolve({ svg: '' }),
          run: () => Promise.resolve(),
          parse: () => ({}),
        }
      `
    }
  }
})

客户端动态加载

tsx
'use client'

import { useEffect, useRef, useState } from 'react'

interface MermaidDiagramProps {
  chart: string
}

export function MermaidDiagram({ chart }: MermaidDiagramProps) {
  const containerRef = useRef<HTMLDivElement>(null)
  const [svg, setSvg] = useState('')

  useEffect(() => {
    // 仅在客户端加载 Mermaid
    import('mermaid').then((mermaid) => {
      mermaid.default.initialize({
        startOnLoad: false,
        theme: 'default',
      })
      
      const id = `mermaid-${Date.now()}`
      
      mermaid.default.render(id, chart).then(({ svg }) => {
        setSvg(svg)
      })
    })
  }, [chart])

  return (
    <div 
      ref={containerRef}
      className="flex justify-center"
      dangerouslySetInnerHTML={{ __html: svg }}
    />
  )
}

最佳实践

1. 图表命名规范

mermaid
%% 使用有意义的节点 ID
graph TD
    %% 好的命名
    UserLogin[用户登录] --> ValidateToken{验证Token}
    ValidateToken -->|有效| Dashboard[显示仪表盘]
    ValidateToken -->|无效| LoginPage[返回登录页]
    
    %% 避免无意义的命名
    %% A --> B --> C

2. 复杂图表拆分

mermaid
%% 主流程图
graph TD
    subgraph 用户认证
        A[登录] --> B[验证]
    end
    
    subgraph 订单处理
        C[创建订单] --> D[支付]
        D --> E[发货]
    end
    
    B --> C

3. 添加注释

mermaid
graph TD
    %% 这是用户登录流程
    A[用户输入] --> B{验证}
    
    %% 验证成功分支
    B -->|成功| C[进入系统]
    
    %% 验证失败分支  
    B -->|失败| D[显示错误]

4. 性能优化

typescript
// 缓存渲染结果
const cache = new Map<string, string>()

export async function renderMermaid(chart: string): Promise<string> {
  if (cache.has(chart)) {
    return cache.get(chart)!
  }
  
  const { svg } = await mermaid.render('id', chart)
  cache.set(chart, svg)
  return svg
}

相关文章

分享: