Typora 反对应用 mermaid 进行绘图,其中就包含流程图。

日常生活中利用流程图的场景并不算太多,在写代码的过程中也更偏差应用伪代码。近期读《计算机程序设计艺术》卷一,上来就是一张流程图,不知怎的,忽然感觉流程图不可或缺。或者流程图搭配伪代码更好。

以上是我强行扯出的写本文的理由。

对于 mermaid

mermaid 泛滥代码绘图工具之一。最先接触的是 PlantUML,过后为了画类图交软件文档,但又不想手动画,于是搜寻到了这个工具。PlantUML 如其名,次要是 UML图,反对的图较少,且依赖较多,配置的步骤较多。

mermaid 尽管没有 PlantUML 那样残缺反对 UML,但根本的图形都能绘制,且感激大前端的倒退,其只是一个“简略”的 .js 文件,导入浏览器导出应用,在 Markdown 这种兼容 HTML 的标记语言下,真心流程。

它反对简略的程序流程图,之所以说简略,是因为残缺的流程切实太过于简单。如果用不上,简单的就搁置吧,mermaid 曾经足够应用了。

装置 mermaid

参考 装置教程。

接触 mermaid 次要是 Typora 自身反对应用 mermaid,我的计划是在 Typora 实现编辑,随后导出 HTML,再到某个反对 Markdown 的平台进行公布。局部网站本身反对 mermaid

绘制到简略流程图如下:

graph LRA([开始]) --> B[/编辑文章/]B --> C{内容实现?}C -->|是| D{再编辑?}C -->|否| BD -->|否| Save[保留]Save --> E{导出?}D -->|是| BE -->|否| End([完结])E -->|是| ExportHTML[/导出HTML/]ExportHTML --> Publish[公布]Publish --> End

Markdown 中应用 mermaid

Typora 应用 mermaid 很简略,间接引入代码即可:

```mermaid```

而咱们须要应用流程图,能够参考文档,引入流程图间接应用 flowchart 或者 graph 关键字,同时须要明确指定流程图的绘制方向,TD 或者 TB 示意从上到下绘制,LR 示意从左到右绘制。(就是上下左右首字母示意)。而 flowchartgraph 可能应用的款式不一样,flowchart 绝对箭头比拟平滑,能用的箭头款式多样,graph 的款式绝对古板。咱们能够尝试一个简略的流程:

```mermaidgraph LRSTART([开始])END([完结])START --> A[测试]A --> isRight{正确?}isRight -->|是| ENDisRight -->|否| A```

上述代码绘制如图:

graph LRSTART([开始])END([完结])START --> A[测试]A --> isRight{正确?}isRight -->|是| ENDisRight -->|否| A

咱们将 graph 替换成 flowchart 如图:

flowchart LRSTART([开始])END([完结])START --> A[测试]A --> isRight{正确?}isRight -->|是| ENDisRight -->|否| A

目前开来仅仅是款式存在区别,而文档中标注 flowchart 属于 Beta 状态,文档中只列举出了更多的箭头反对和子流程图反对。这并非咱们应该关注的重点,因此应用那个关键字,齐全在于集体爱好。flowchart 虽在文字上具备较为强烈的识别性,但 graph 绘制出了图像更清晰,在 Typora 中也有代码高亮,且 flowchat 提供的性能也用不上,因而后续应用 graph 作为关键字。

流程图根底

流程图的次要功能在于绝对文字能更直观形容某件事情的过程。

而应用代码实现流程图的构建,更像是伪代码去即时展示逻辑,当然也不便不相熟代码的人间接了解代码的基本思路(不晓得这有啥用)。绘制流程图,理清本人的逻辑是最次要的。

而人与人交换都须要一个标准,流程图自身有一个标准。

流程图符号

此处将列举惯例符号和 mermaid 对应的代码。

mermaid 并没有规定那个符号表明哪个意思,只是表述了该符号的形态。

1. 流程

示意执行或者解决某些工作。

mermaid 中流程的符号很简略,就是中括号 [],示例如下:

graph LRA[流程1]B[流程2]A --> B

其成果如下:

graph LRA[流程1]B[流程2]A --> B

mermaid 中,流程是一个根本的 node,它的形态是矩形,还有圆角矩形(应用 () 表述),两者区别不大,没有特定的要求,但 [] 更容易输出,因此应用 () 的必要不大。

2. 开始或完结

在 文档 中,所有开始或结符号都是应用流程间接代替的,并没有给出具体的要求。但流程图标准当中,开始和完结该当有别与流程的形态。在 mermaid 中应用 ([]) 能够绘制一个区别于流程的图形,如下:

graph LRSTART([开始])END([完结])START --> END

后果如下:

graph LRSTART([开始])END([完结])START --> END

文档 中,称之为 A stadium-shaped node,其形态相似于体育场。而开始和完结只有一个,在初始时将其定义好,后续能够间接应用。

特地留神,定义完结的时候,切勿应用小写 end ,否则可能倒置流程图绘制中断

3. 断定

断定是对立的,mermaid 应用 {} 示意。如下:

graph LRSTART([开始])END([完结])Flow[流程]isTrue{正确?}isTrue -->|是| ENDisTrue -->|否| FlowSTART --> Flow --> isTrue

成果如下:

graph LRSTART([开始])END([完结])Flow[流程]isTrue{正确?}isTrue -->|是| ENDisTrue -->|否| FlowSTART --> Flow --> isTrue

如代码所示,咱们定义 isTrue 作为一个断定框,其内容是 “正确?”,而正确与否的之后的流程如何解决很简略,间接在下一个前流程应用 |是/否| 即可。

4. 子流程

如其名,是一部分流程的汇合,次要功能在于简化整个流程图。而其外部的流程能够以附件形式展现到另一个流程图中。相似于子程序,mermaid 中为 A node in a subroutine shape。

代码如下:

graph LRid[[子流程]]

成果如下:

graph LRid[[子流程]]

即应用两个中括号示意,[[]]

5. 数据库

示意存储地位,属于扩大。

代码如下:

graph LRDB[(数据库)]

成果如下:

graph LRDB[(数据库)]

符号是 [()]

6. 页面内援用

即接口,示意两个流程图间的接口。

用处在于:

  1. 连贯到下一页;
  2. 防止流线穿插;
  3. 防止流线太长;

代码如下:

graph LROUT((接口))

成果如下:

graph LROUT((接口))

符号是 (())

7. 输出/输入

呈现输出或者输入的时候,应该区别于流程。

代码如下:

graph LRinput[/输出/]

成果如下:

graph LRinput[/输出/]

mermaid 还有反向的平行四边形,其符号与上述相同,[\\],成果如:

graph LRinput[\输出\]

但咱们个别应用第一个。

8. (附加)箭头款式

流程图仿佛只有实线箭头,其余箭头很少见到,但 mermaid 提供了不少款式的箭头。

代码如下:

graph LRA --- |直线| BA ----- |直线长度是 - 符号定义 - 长度3-5| B0A --> |直线箭头| B1A ---> |长直线箭头| B2A === |粗线| B3A ==> |粗线箭头| B4A -.- |虚线| B5A -.-> |虚线箭头| B6

成果如下:

graph LRA --- |直线| BA ----- |直线长度是 - 符号定义 - 长度3-5| B0A --> |直线箭头| B1A ---> |长直线箭头| B2A === |粗线| B3A ==> |粗线箭头| B4A -.- |虚线| B5A -.-> |虚线箭头| B6

能够依据本人的需要应用。

当然,mermaid 还提供许多其余图形,包含梯形等等。这些请参考文档。

接下来咱们绘制下流程图的根底构造。

流程图的根本构造

流程图有三大根本构造,别离是:程序构造、抉择构造和循环构造,程序的形成也根本如此。在流程图绘制标准中,有规定这些构造的绘制办法,而利用 mermaid 绘制进去的图形仿佛并不那么标准。

1. 程序构造

程序构造即按程序先后顺序执行,从上往下/从左往右,一个一个执行。最根本的构造。

代码如下:

graph LRSTART([开始])END([完结])START --> AA --> BB --> CC --> END

成果如下:

graph LRSTART([开始])END([完结])START --> AA --> BB --> CC --> END

2. 抉择构造

抉择,即分支,即 if-else 构造:

graph TDisTrueA{条件A}isTrueB{条件B}isTrueC{条件C}A([开始]) --> isTrueAisTrueA --> |是| A1[步骤1]isTrueA --> |否| A2[步骤2]A1 --> AE([完结])A2 --> AEB([开始]) --> isTrueBisTrueB --> |是| BE([完结])isTrueB --> |否| B1[步骤3]B1 --> BEC([开始]) --> isTrueCisTrueC --> |是| C1[步骤4]isTrueC --> |否| CE([完结])C1 --> CE

成果如下:

graph TDisTrueA{条件A}isTrueB{条件B}isTrueC{条件C}A([开始]) --> isTrueAisTrueA --> |是| A1[步骤1]isTrueA --> |否| A2[步骤2]A1 --> AE([完结])A2 --> AEB([开始]) --> isTrueBisTrueB --> |是| BE([完结])isTrueB --> |否| B1[步骤3]B1 --> BEC([开始]) --> isTrueCisTrueC --> |是| C1[步骤4]isTrueC --> |否| CE([完结])C1 --> CE

能够看到,尽管咱们依照程序编写代码,但 条件B条件C 的绘制中能够看出有步骤的状况在右,若两边都蕴含步骤,则按程序绘制。因为“是否”是依照 |文字| 嵌入的,则依照程序缺省行为执行绘制。

3. 循环构造

顾名思义,在肯定的条件下,其会实现反复动作。

循环构造又有当型和直到型,能够看作 whiledo while(!条件)

代码:

graph TDisTrue1{当型判断?}isTrue2{直到型判断?}A([开始]) --> isTrue1isTrue1 --> |是| A1[步骤1] --> isTrue1isTrue1 --> |否| AE([完结])B([开始]) --> B1[步骤2]B1 --> isTrue2isTrue2 --> |是| BE([完结])isTrue2 --> |否| B1

成果:

graph TDisTrue1{当型判断?}isTrue2{直到型判断?}A([开始]) --> isTrue1isTrue1 --> |是| A1[步骤1] --> isTrue1isTrue1 --> |否| AE([完结])B([开始]) --> B1[步骤2]B1 --> isTrue2isTrue2 --> |是| BE([完结])isTrue2 --> |否| B1

都到这了,不思考什么画图标准了……

案例

来几个经典的案例。

1. 猜数字

猜数字 number,为随机数。

  1. 输出数字n
  2. 判断大小,提醒大或者小
  3. 猜对提醒,是否持续
  4. 不持续退出,持续则从新生成 number, 反复步骤1-4
graph TDSTART([开始])END([完结])%% 该数字非输出,由系统生成gen_number[生成随机数字 number--系统生成]input_n[/输出数字n/]%% 定义判断条件n_eq_number?{ n == number? }n_lt_number?{ n < number? }continue?{ 持续? }%% 提醒ALERT_gt[/提醒数字大了/]ALERT_eq[/提醒数字正确/]ALERT_lt[/提醒数字小了/]START --> gen_number --> input_ninput_n --> n_eq_number?n_eq_number? --> |是| ALERT_eq --> continue?n_eq_number? --> |否| n_lt_number?n_lt_number? --> |是| ALERT_ltn_lt_number? --> |否| ALERT_gtn_lt_number? --> input_ncontinue? --> |是| gen_numbercontinue? --> |否| END
graph TDSTART([开始])END([完结])%% 该数字非输出,由系统生成gen_number[生成随机数字 number--系统生成]input_n[/输出数字n/]%% 定义判断条件n_eq_number?{ n == number? }n_lt_number?{ n < number? }continue?{ 持续? }%% 提醒ALERT_gt[/提醒数字大了/]ALERT_eq[/提醒数字正确/]ALERT_lt[/提醒数字小了/]START --> gen_number --> input_ninput_n --> n_eq_number?n_eq_number? --> |是| ALERT_eq --> continue?n_eq_number? --> |否| n_lt_number?n_lt_number? --> |是| ALERT_ltn_lt_number? --> |否| ALERT_gtn_lt_number? --> input_ncontinue? --> |是| gen_numbercontinue? --> |否| END

逻辑有待优化。

2. 注册登录逻辑

1. 注册

graph TDSTART([开始])END([完结])input[/输出帐号密码/]START --> inputinput --> pw_pair?{明码是否合乎格局?}pw_pair? --> |否| inputpw_pair? --> |是| user_pair?{帐号是否合乎格局?}user_pair? --> |是| get_code[获取验证码]user_pair? --> |否| inputget_code --> input_code[/输出验证码/]input_code --> code_pair?{验证码是否正确?}code_pair? --> |是| success[注册胜利]success -.- |存储| database[(用户数据库)]code_pair? --> |否| 记录验证码谬误次数 --> try_more?{尝试次数过多?}try_more? --> |是| will_END[限度获取验证码次数,禁止注册] --> ENDtry_more? --> |否| en_time?{是否距离60s获取验证码?}en_time? --> |是| get_codeen_time? --> |否| wait_time[期待60s] --> get_codesuccess --> END
graph TDSTART([开始])END([完结])input[/输出帐号密码/]START --> inputinput --> pw_pair?{明码是否合乎格局?}pw_pair? --> |否| inputpw_pair? --> |是| user_pair?{帐号是否合乎格局?}user_pair? --> |是| get_code[获取验证码]user_pair? --> |否| inputget_code --> input_code[/输出验证码/]input_code --> code_pair?{验证码是否正确?}code_pair? --> |是| success[注册胜利]success -.- |存储| database[(用户数据库)]code_pair? --> |否| 记录验证码谬误次数 --> try_more?{尝试次数过多?}try_more? --> |是| will_END[限度获取验证码次数,禁止注册] --> ENDtry_more? --> |否| en_time?{是否距离60s获取验证码?}en_time? --> |是| get_codeen_time? --> |否| wait_time[期待60s] --> get_codesuccess --> END

2. 登录

graph TDsubgraph db [数据库]db_user[(用户数据库)]db_login[(已登录用户数据库)]endSTART([开始])END([完结])input[/输出帐号密码/]START --> inputinput --> get_code[获取验证码]get_code --> input_code[/输出验证码/]input_code --> code_pair?{验证码是否正确?}code_pair? --> |是| pw_pair[从数据库匹配帐号密码]pw_pair -.- |查找| db_userpw_pair --> pw_pair?{帐号密码匹配?}pw_pair? --> |是| success[登录胜利]success -.- |存储| db_loginpw_pair? --> |否| inputcode_pair? --> |否| 记录验证码谬误次数 --> try_more?{尝试次数过多?}try_more? --> |是| will_END[限度获取验证码次数,禁止登录] --> ENDtry_more? --> |否| en_time?{是否距离60s获取验证码?}en_time? --> |是| get_codeen_time? --> |否| wait_time[期待60s] --> get_codesuccess --> END
graph TDsubgraph db [数据库]db_user[(用户数据库)]db_login[(已登录用户数据库)]endSTART([开始])END([完结])input[/输出帐号密码/]START --> inputinput --> get_code[获取验证码]get_code --> input_code[/输出验证码/]input_code --> code_pair?{验证码是否正确?}code_pair? --> |是| pw_pair[从数据库匹配帐号密码]pw_pair -.- |查找| db_userpw_pair --> pw_pair?{帐号密码匹配?}pw_pair? --> |是| success[登录胜利]success -.- |存储| db_loginpw_pair? --> |否| inputcode_pair? --> |否| 记录验证码谬误次数 --> try_more?{尝试次数过多?}try_more? --> |是| will_END[限度获取验证码次数,禁止登录] --> ENDtry_more? --> |否| en_time?{是否距离60s获取验证码?}en_time? --> |是| get_codeen_time? --> |否| wait_time[期待60s] --> get_codesuccess --> END

在绝对简单的逻辑下,整体绘图有些芜杂。

小结

流程图不适用于过于简单的逻辑,对于简单的逻辑会有些表现力有余。前一章的登录界面我绘制了屡次,包含一张图体现登录注册登出等性能(应用 subgraph )当绘制实现之后,只管伪代码很容易了解,图像却盘根错节。

流程图更实用于体现逻辑关系,对于程序中一个性能进行形容(面向过程),而状态变动却不好表白。这又得引出一个状态流图。

而面向对象的类图又须要 UML 进行形容。

目前流程图可用于简略的繁多性能形容中,其余须要后续再进行总结。一个残缺的利用不会这么简略。