我们需要绘图
图,通过描述关系,简化系统分析。
随着基础设施的不断完善,各行各业的产品日益复杂,系统链路不断延长。
我们需要一种更加清晰的方式来描述这些系统的内部结构和关系。
图形化可以有效地简化系统分析,消除冗余信息,帮助我们理解复杂的交互和流程。
好的图能够降低复杂度,进而减少理解成本,增加信息传递效率。
画图的抽象过程,也是一次锤炼抽象思维的过程。
复杂系统实例:
- 低代码
- 智能制造系统,集成人工智能和机器学习,自动化生产调度、质量控制、设备维护
- 工业物联网平台,连接和管理工业设备,通过数据采集和分析提升操作效率和智能决策
- 供应链管理,利用区块链提高供应链的透明和可追溯性
- 持续集成/持续部署(CI/CD)工具,容器化编排工具
- 基于 npm 的软件构建体系,如 webpack 的编排流程
为什么选择mermaid
在众多绘图工具中,Mermaid 是一个独特的选择,它通过简单的文本描述生成图形,使得可视化过程更加直观和高效。它的优点包括:
- 易于使用:只需用文本描述结构和关系,无需学习复杂的绘图技能。
- 快速迭代:修改描述后,图形自动更新,适合快速迭代和反馈。
- 集成方便:可以轻松嵌入到文档(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: 意外转账成功!😭😭😭