Published on

给 Hugo 博客添加 PlantUML 支持

Authors
  • avatar
    Name
    ttyS3
    Twitter

PlantUML 是最近才了解到的, 实际上这个东西存在已经很多年了.

之前有用过 mermaid, 但是 mermaid 的 flowchart 写起来很蛋疼.

PlantUML 的 activity diagram 对多行文本和 note 支持更好, 同时不用写太多的箭头和 A, B, C 这种标记.

mermaid 要支持多行文本, 当前只能用 foo1<br/>foo2<br/>foo3 的方式.

另一方面, A, B, C 这种标记, 当你突然想在中间插入一个的时候, 就会很蛋疼.

添加到 Hugo 模板

这里我并没有采用 Front Matter 参数的方式来确定是否加载 plantuml-encoder js 文件, 因此老灯觉得这样太麻烦.

因此是通过判断内容是否包含 plantuml 代码块来实现动态加载.


  const loadScript = (url, onloadFunction) => {
    var newScript = document.createElement("script");
    newScript.onerror =  (oError) => {
      throw new URIError("The script " + oError.target.src + " didn't load correctly.");
    };
    if (onloadFunction) { newScript.onload = onloadFunction; }
    document.head.insertAdjacentElement('beforeend', newScript);
    newScript.src = url;
  }

    const loadPlantUMLOnNeed = () => {
    let plantumlPrefix = "language-plantuml";

    if (document.querySelectorAll("[class^=" + plantumlPrefix + "]").length > 0) {
      loadScript('https://cdn.jsdelivr.net/gh/jmnote/[email protected]/dist/plantuml-encoder.min.js', () => {
        (function(){
          Array.prototype.forEach.call(document.querySelectorAll("[class^=" + plantumlPrefix + "]"), function(code){
            let image = document.createElement("IMG");
            image.loading = 'lazy'; // Lazy loading
            image.src = 'http://www.plantuml.com/plantuml/svg/~1' + plantumlEncoder.encode(code.innerText);
            code.parentNode.insertBefore(image, code);
            code.style.display = 'none';
          });
        })();

        console.log("PlantUML init done");
      })
    }
  }

  window.addEventListener('load', function(event) {
    // load PlantUML
    loadPlantUMLOnNeed();
  })

流程图

注: activity diagram 实际上和 flowchart 不是一个东西. 但是用 activity diagram 完全可以表现 flowchart 所要表现的东西. 实际上 activity diagram 比 传统意义上的 flowchart 更强大.

Activity diagrams can be regarded as a form of a structured flowchart combined with a traditional data flow diagram. Typical flowchart techniques lack constructs for expressing concurrency.[5] However, the join and split symbols in activity diagrams only resolve this for simple cases; the meaning of the model is not clear when they are arbitrarily combined with decisions or loops. (活动图可以视为是种结构化的流程图,并且结合了传统的数据流程图。典型的流程图技术中缺乏表示并发性。不过,活动中的分离和汇合符号只解决了在简单情形下的应用,若是配合决策或是循环,其意义较不清楚) https://en.wikipedia.org/wiki/Activity_diagram

以 mermaid 默认的 flowchart example 为例, 其代码如下:

graph TD
    A[Christmas] -->|Get money| B(Go shopping)
    B --> C{Let me think}
    C -->|One| D[Laptop]
    C -->|Two| E[iPhone]
    C -->|Three| F[fa:fa-car Car]

效果如下:

而 PlantUML 写起来就更简单了.

用 UML 的活动图表示:

@startuml
: Christmas;
-> Get money;
: Go shopping;
switch (Let me think)
    case (One)
    : Laptop;
    detach
    case (Two)
    : iPhone;
    detach
    case (Three)
    : Car;
    detach
@enduml

注意, PlantUML 默认是连接的, 这里为了实现上面 mermaid 一样的图样效果, 特意加上了额外的 detach (一般情况下都不需要手动加 detach), 同时也没有写 startstop标记.

时序图

sequenceDiagram
    Alice->>+John: Hello John, how are you?
    Alice->>+John: John, can you hear me?
    John-->>-Alice: Hi Alice, I can hear you!
    John-->>-Alice: I feel great!

用 PlantUML 可以完全复现如下:

```plantuml
    Alice -> John: Hello John, how are you?
    activate John
    
    Alice -> John: John, can you hear me?
    activate John
    John--> Alice: Hi Alice, I can hear you!
    deactivate John
    
    John--> Alice: I feel great!
    deactivate John
```

refs

https://plantuml.com/activity-diagram-beta

https://plantuml.com/sequence-diagram

https://mogeko.me/posts/zh-cn/083/