Mermaid能做到什么程度!

157 阅读2分钟

我们需要绘图

图,通过描述关系,简化系统分析。

随着基础设施的不断完善,各行各业的产品日益复杂,系统链路不断延长。

我们需要一种更加清晰的方式来描述这些系统的内部结构和关系。

图形化可以有效地简化系统分析,消除冗余信息,帮助我们理解复杂的交互和流程。

好的图能够降低复杂度,进而减少理解成本,增加信息传递效率。

画图的抽象过程,也是一次锤炼抽象思维的过程。

复杂系统实例:

  • 低代码
  • 智能制造系统,集成人工智能和机器学习,自动化生产调度、质量控制、设备维护
  • 工业物联网平台,连接和管理工业设备,通过数据采集和分析提升操作效率和智能决策
  • 供应链管理,利用区块链提高供应链的透明和可追溯性
  • 持续集成/持续部署(CI/CD)工具,容器化编排工具
  • 基于 npm 的软件构建体系,如 webpack 的编排流程

为什么选择mermaid

在众多绘图工具中,Mermaid 是一个独特的选择,它通过简单的文本描述生成图形,使得可视化过程更加直观和高效。它的优点包括:

  1. 易于使用:只需用文本描述结构和关系,无需学习复杂的绘图技能。
  2. 快速迭代:修改描述后,图形自动更新,适合快速迭代和反馈。
  3. 集成方便:可以轻松嵌入到文档(markdown)和项目中,提高协作效率。

mermaid 能够在 markdown 格式中直接展示。方便技术的传递。

对于复杂的泳道图,可能使用 processon飞书画板 更好,不过其他场景下,mermaid 将是一个不错的选择。

使用案例

🍀🍀🍀 下面我们来看看 mermaid 能做到什么程度?

接口流程图1

flowchart TB
    %% 用户操作子图
    subgraph 用户操作
        %% 新增一级
        N1["新增一级"]:::userOperation
        %% 新增二级
        N2["新增二级"]:::userOperation
        %% 删除一级
        D1["删除一级"]:::userOperation
        %% 删除二级
        D2["删除二级"]:::userOperation
        %% 修改
        U["修改"]:::userOperation
        %% 顺序操作
        S["顺序操作"]:::userOperation
    end

    %% 接口请求子图
    subgraph 接口请求
        %% 新增一级接口请求
        N1_checkName["checkName"]:::request
        N1_save["save"]:::request
        N1_detail["detail"]:::request

        %% 新增二级接口请求
        N2_checkName["checkName"]:::request
        N2_save["save"]:::request
        N2_detail["detail"]:::request

        %% 删除一级接口请求
        D1_checkUseStatus["checkUseStatus"]:::request
        D1_delete["delete"]:::request

        %% 删除二级接口请求
        D2_checkUseStatus["checkUseStatus"]:::request
        D2_delete["delete"]:::request

        %% 修改接口请求
        U_checkName["checkName"]:::request
        U_save["save"]:::request

        %% sort 请求
        common_sort["sort"]:::highlight
    end

    %% 新增一级流程
    N1 -->|检查名称| N1_checkName
    N1_checkName -->|保存| N1_save
    N1_save -->|详情| N1_detail
    N1_detail -->|排序| common_sort

    %% 新增二级流程
    N2 -->|检查名称| N2_checkName
    N2_checkName -->|保存| N2_save
    N2_save -->|详情| N2_detail
    N2_detail -->|排序| common_sort

    %% 删除一级流程
    D1 -->|检查使用状态| D1_checkUseStatus
    D1_checkUseStatus -->|删除| D1_delete
    D1_delete -->|排序| common_sort

    %% 删除二级流程
    D2 -->|检查使用状态| D2_checkUseStatus
    D2_checkUseStatus -->|删除| D2_delete

    %% 修改流程
    U -->|检查名称| U_checkName
    U_checkName -->|保存| U_save

    %% 顺序操作流程
    S -->|排序| common_sort

    %% 定义样式
    classDef userOperation fill:#a5d6a7,stroke:#388e3c,stroke-width:2px,stroke-dasharray: 5 5,font-weight:bold;
    classDef request fill:#ffe0b2,stroke:#fb8c00,stroke-width:2px,stroke-dasharray: 5 5,font-weight:bold;
    classDef highlight fill:#fff9c4,stroke:#fbc02d,stroke-width:4px,font-weight:bold;
    classDef errorBox fill:#ffccbc,stroke:#d32f2f,stroke-width:2px,font-weight:bold,font-style:italic;

    %% 备注
    E1["Error: 当前 sort 数据为所有类目的排序,但其实我们只需要增量数据。\n数据结构: [{'id':100194,'sortNum':3}, {'id':100197,'sortNum':4}, {'id':100001,'sortNum':5}, {'id':100276,'sortNum':6}, {'id':100249,'sortNum':7}, {'id':100169,'sortNum':8}]"]:::errorBox
    common_sort -.-> E1

接口流程图2

flowchart TB
    %% 用户操作子图
    subgraph 用户操作
        %% 新增一级
        N1["新增一级"]:::userOperation
        %% 新增二级
        N2["新增二级"]:::userOperation
        %% 删除一级
        D1["删除一级"]:::userOperation
        %% 删除二级
          D2["删除二级"]:::userOperation
          %% 修改
        U["修改"]:::userOperation
        %% 顺序操作
        S["顺序操作"]:::userOperation
    end

    %% 接口请求子图
    subgraph 接口请求
        %% 新增一级接口请求
        N1_checkName["checkName"]:::request
        N1_save["save"]:::request
        N1_detail["detail"]:::request

        %% 新增二级接口请求
        N2_checkName["checkName"]:::request
        N2_save["save"]:::request
        N2_detail["detail"]:::request

        %% 删除一级接口请求
        D1_checkUseStatus["checkUseStatus"]:::request
        D1_delete["delete"]:::request

        %% 删除二级接口请求
        D2_checkUseStatus["checkUseStatus"]:::request
        D2_delete["delete"]:::request

        %% 修改接口请求
        U_checkName["checkName"]:::request
        U_save["save"]:::request

        %% sort 请求
        common_sort["sort"]:::highlight
    end

    %% 修改部分子图
    subgraph 修改部分 [修改部分]
        direction TB
        common_sort -.-> M1
        N1_detail -.-> common_sort_old
        N2_detail -.-> common_sort_old
        D1_delete -.-> common_sort_old
    end

    %% 新增一级流程
    N1 -->|检查名称| N1_checkName
    N1_checkName -->|保存| N1_save
    N1_save -->|详情| N1_detail

    %% 新增二级流程
    N2 -->|检查名称| N2_checkName
    N2_checkName -->|保存| N2_save
    N2_save -->|详情| N2_detail

    %% 删除一级流程
    D1 -->|检查使用状态| D1_checkUseStatus
    D1_checkUseStatus -->|删除| D1_delete

    %% 删除二级流程
    D2 -->|检查使用状态| D2_checkUseStatus
    D2_checkUseStatus -->|删除| D2_delete

    %% 修改流程
    U -->|检查名称| U_checkName
    U_checkName -->|保存| U_save

    %% 顺序操作流程
    S -->|排序| common_sort

    %% 定义样式
    classDef userOperation fill:#a5d6a7,stroke:#388e3c,stroke-width:2px,stroke-dasharray: 5 5,font-weight:bold;
    classDef request fill:#ffe0b2,stroke:#fb8c00,stroke-width:2px,stroke-dasharray: 5 5,font-weight:bold;
    classDef highlight fill:#fff9c4,stroke:#fbc02d,stroke-width:4px,font-weight:bold;
    classDef modifyBox fill:#ccffcc,stroke:#009933,stroke-width:2px,font-weight:bold,font-style:italic;

    %% 备注
    M1["改动点1:\n当前 sort 请求的数据结构变更,\n只输出排序中需要变更的2条数据"]:::modifyBox
    common_sort_old["改动点2:\n原新增、删除需sort请求\n现已删除"]:::modifyBox

    %% 将修改部分子区块虚线化
    class 修改部分 dashBox;
    classDef dashBox stroke-dasharray: 5 5;

sdk设计

flowchart TD
    %% 定义样式
    classDef userAction fill:#FFDDC1,stroke:#FF7F50,stroke-width:2px;
    classDef browserEvent fill:#DAF7A6,stroke:#28B463,stroke-width:2px;
    classDef sdkLogic fill:#AED6F1,stroke:#2874A6,stroke-width:2px;

    %% 用户操作部分
    subgraph UserActions [用户操作]
      U1[访问页面]:::userAction
      U2[前进按钮]:::userAction
      U3[后退按钮]:::userAction
      U6[页面跳转]:::userAction
      U4[发送 xhr 请求]:::userAction
      U5[发送 fetch 请求]:::userAction
    end

    %% 浏览器层,包裹页面加载和页面加载中的事件
    subgraph BrowserLayer [浏览器层]
      %% 页面加载完
        subgraph PageLoading [页面加载完]
          L1([开始加载页面]):::browserEvent
          L2([onload 事件触发]):::browserEvent
          L3([LCP 上报时点]):::browserEvent
        end
      %% 
        subgraph PageEvents [页面运行中]
          PE1[onerror 事件触发]:::browserEvent
          PE2[页面路由切换]:::browserEvent
          PE3[页面请求]:::browserEvent
        end
    end

    %% SDK 检测逻辑部分
    subgraph SDK [SDK 检测]
        SDKDetect[白屏检测]:::sdkLogic
    end

    %% 连接用户操作与页面加载阶段
    PageLoading --> PE1
    U1 --> PageLoading
    U2 --> PE2
    U3 --> PE2
    U6 --> PE2
    U4 --> PE3
    U5 --> PE3

    %% 页面加载阶段与 SDK 检测逻辑的连接
    L2 --> SDKDetect
    L3 --> SDKDetect
    PE1 --> SDKDetect
    PE2 --> SDKDetect
    PE3 --> SDKDetect

Vue组件逻辑分析1

    flowchart TB
      %% 输入
      subgraph 输入
        classDef inputClass fill:#FFDD99,stroke:#333,stroke-width:2px;
        classDef noteClass fill:#FFCCCC,stroke:#FF0000,stroke-width:2px,stroke-dasharray: 5 5;
        I4[聚焦事件focus]:::inputClass
        I1[输入事件input]:::inputClass
        I3[失焦事件blur]:::inputClass
        I2[粘贴事件paste]:::inputClass
        note1[[输入结构为 domInnerHTML]]:::noteClass
      end

      %% 事件响应
      subgraph 事件响应
        classDef eventClass fill:#A3E4D7,stroke:#333,stroke-width:2px;
        classDef highlightedClass fill:#FF6666,stroke:#FF0000,stroke-width:2px,stroke-dasharray: 5 5;
        classDef errorClass fill:none,stroke:#FF0000,stroke-width:2px,stroke-dasharray: 5 5;
        classDef errorBoldClass fill:#fff2ff,stroke:red;
        I1 --> |inputChangeHandler| E1[保存光标位置]:::eventClass
        E1 --> E2[判断是否是动参开始符号]:::eventClass
        E2 --> |否| E3[隐藏选择框]:::eventClass
        E3 --> E31[进行普通输入]:::eventClass
        E31 --> E6
        E2 --> |是| E4[打开动参选择框]:::eventClass
        E4 --> E5[确认动参-创建高亮节点]:::eventClass
        E5 --> E6[插入到指定位置]:::eventClass
        I2 --> |pasteHandler| E7[遍历所有复制的节点]:::eventClass
        E7 --> E71[根据标签或纯文本进行处理]:::eventClass
        E71 --> E8[复制到尾部]:::highlightedClass
        I4 --> |focusHandler| E10[修改内部blur为false]:::eventClass
        I3 --> |blurHandler| E9[修改内部blur为true]:::eventClass

        %% ErrorBox
        subgraph ErrorBox [ ]
          E8
          N1[错误: 我们不希望复制到尾部,而是复制到指定位置]:::errorBoldClass
        end
        class ErrorBox errorClass;

      end

      %% 输出
      subgraph 输出
        classDef outputClass fill:#F9E79F,stroke:#333,stroke-width:2px;
        E6 --> O1[domInnerHTML]:::outputClass
        E8 --> O1:::highlightedClass
        E9 --> O1:::outputClass
      end

      %% 外部连接
      classDef linkClass stroke:#3498DB,stroke-width:2px;
      classDef noteClass fill:#FFCCCC,stroke:#FF0000,stroke-width:2px;

      I4 --> I1:::linkClass
      I4 --> I2:::linkClass
      I4 --> I3:::linkClass

      %% Subgraph Borders Example
      classDef subgraphClass stroke:#000000,stroke-width:2px;
      class 输入,事件响应,输出 subgraphClass;

Vue组件逻辑分析2

    flowchart TB

      %% 输入
      subgraph 输入
        classDef inputClass fill:#FFDD99,stroke:#333,stroke-width:2px;
        classDef noteClass fill:#FFCCCC,stroke:#FF0000,stroke-width:2px,stroke-dasharray: 5 5;
        I1[聚焦事件focus]:::inputClass
        I3[输入事件input]:::inputClass
        I2[失焦事件blur]:::inputClass
        I4[键盘keydown事件]:::inputClass
        note[[输入结构为 Text]]:::noteClass
      end

      %% 事件处理
      subgraph 事件处理
        classDef eventClass fill:#A3E4D7,stroke:#333,stroke-width:2px;
        classDef highlightedClass fill:#FF6666,stroke:#FF0000,stroke-width:2px,stroke-dasharray: 5 5;
        classDef errorClass fill:none,stroke:#FF0000,stroke-width:2px,stroke-dasharray: 5 5;
        classDef errorBoldClass fill:#fff2ff,stroke:red;
        I3 --> |handleInput|H1[判断是否拼音输入法输入]:::eventClass
        H1 --> |否| H1N1[判断是否是动参开始符号]:::eventClass
        H1N1 --> |否| H1N1N[隐藏选择框]:::eventClass
        H1N1 --> |是| H1N1Y[打开动参选择框]:::eventClass
        H1N1N --> H1N1N1[进行普通输入]:::eventClass
        H1N1N1 --> H2:::eventClass
        H1 --> |是| H1Y[获取拼音输入时的英文临时插入]:::eventClass
        H1Y --> H1Y1[拼音确认时插入中文内容]:::eventClass
        H1N1Y --> H2[input层写入展示内容]
        H2 --> |domInnerHTML to text| H2X[添加到指定位置]:::eventClass
        H2X --> H2XY[div展示层处理动参和展示]
        I2 --> |onFocus| H3[对外暴露blur事件和位置]:::eventClass
        I4 --> |onKeydown| H4[键盘删除/回车/粘贴/输入事件]:::eventClass
        H4 --> H41[更新div层高度]:::eventClass
        H41 --> |domInnerHTML to text|H2X
        H1Y1 --> |domInnerHTML to text|H2X
        I1 --> |focusHandler| H5[对外暴露focus事件]:::eventClass
      end

      %% 输出
      subgraph 输出
          classDef highlightedClass fill:#FF6666,stroke:#FF0000,stroke-width:2px,stroke-dasharray: 5 5;
        classDef outputClass fill:#F9E79F,stroke:#333,stroke-width:2px;
        H2XY --> O1[Text]:::highlightedClass;
        O1:::outputClass
      end

      %% 外部连接
      classDef linkClass stroke:#3498DB,stroke-width:2px;
      classDef noteClass fill:#FFCCCC,stroke:#FF0000,stroke-width:2px;

      I1 --> I2:::linkClass
      I1 --> I3:::linkClass
      I1 --> I4:::linkClass

        %% Subgraph Borders Example
      classDef subgraphClass stroke:#089e8a,stroke-width:2px;
      class 输入,事件响应,输出 subgraphClass;

订单支付系统

    sequenceDiagram 
    autonumber
    actor 用户  
    participant 订单服务  
    participant 支付服务  
    participant 银行服务  
    participant 履约服务  

    用户->>订单服务: 创建订单 
    Note right of 用户: 此处需要注意库存问题
    订单服务->>支付服务: 发送支付请求  
    支付服务->>银行服务: 银行扣款  
    银行服务-->>支付服务: 返回扣款结果  
    支付服务-->>订单服务: 更新订单状态
    Note over 订单服务,支付服务: 此处需要注意幂等性 
    订单服务->>履约服务: 发货
    Note right of 银行服务: 此处需要注意下游履约情况
    履约服务-->>用户: 完成发货
    用户->>订单服务: 确认收货  
    订单服务->>订单服务: 更新订单状态为已收货  
    Note right of 订单服务: 此处需要保障事务
    订单服务->>订单服务: 返回更新后的订单状态  
    订单服务-->>用户: 返回更新后的订单状态

cookie之广告联盟(收割用户)

sequenceDiagram
			Actor user as 用户
      participant news as 新闻站(news.com)
			participant sports as 体育站(sports.com)
			participant shop as 购物站(shop.com)
			participant ads as 广告联盟(ads.com)
			%% 首次访问(生成 Cookie)%%
			Note over user,ads: 🎯 阶段1,首次访问,生成 Cookie
			user ->>+ news: 访问新闻站(内嵌广告联盟链接),无 cookie
			news ->>+ ads: 请求广告链接
			ads -->>- news: 返回广告 + Set-Cookie,uid = 123
			news -->>- user: 返回新闻+广告内容(浏览器留下cookie)
			Note over user,ads: ✅ cookie生效,收集行为数据
			user ->>+ sports: 访问体育页Cookie uid = 123
			sports ->>+ ads: 请求广告(uid = 123,like = sports)
			ads -->>- sports: 记录用户喜欢体育,返回体育广告
			sports -->>- user: 显示体育 + 广告
			Note over user,ads: 💰 用户行为 -> 商业应用
			user ->>+ shop: 访问购物站 Cookie uid = 123 like = sports
			shop ->>+ ads: 请求购物广告
			ads ->>- shop: 记录浏览搜索行为,积累数据
			shop -->>- user: 显示购物 + 广告
			Note over ads: 用户画像构建逐渐完善
			ads ->> ads: Cookie uid = 123 like = sports shop = shoots
			Note over user: ❗用户处于数据围栏,等待收割

			
			

CSRF攻击

sequenceDiagram
  actor user as 用户
  participant bank as 银行网站
  participant hack as 黑客网站
  Note over user,bank: 正常流程,首次访问生成 Cookie
  user->>bank: 访问银行网站
  bank->>user: 返回登录页面
  user->>bank: 输入用户名和密码
  bank->>user: 登录成功
  %% 访问黑客网站%%
  Note over user,hack: 访问恶意网站
  user->>hack: 访问黑客网站
  hack ->> user: 返回攻击内容
    Note over user,hack: 攻击内容:诱导点击链接/隐藏表单提交/JS静默请求
  user->>bank: 点击链接/提交表单/执行JS!!!
  bank->>user: 意外转账成功!😭😭😭

推荐阅读

吴彤——复杂性:正在发生的革命-清华大学