Trình tạo lưu đồ Mermaid
Lưu đồ cho thấy một quy trình diễn ra thế nào: có những bước nào, rẽ nhánh ở đâu và các nhánh nhập lại ở đâu. Nó hợp khi chủ đề là thứ tự của các quyết định — một lần phát hành, đường đi của một yêu cầu, một luồng phê duyệt. Nếu chủ đề là ai nói với ai vào lúc nào thì sơ đồ tuần tự tốt hơn.
Quy trình phát hành với hai nhánh lỗi
Hầu hết lưu đồ trên trang này đều bắt đầu từ hình dạng này: một đường thuận lợi chạy thẳng và các hình thoi quyết định rẽ ra từ đó. Hãy để ý dấu nháy ở nút áp chót. Đặt dấu nháy quanh nhãn có chứa ngoặc đơn là bắt buộc, và quên điều đó là lỗi phổ biến nhất.
flowchart TD
Push[Đẩy lên nhánh main] --> Lint[Phân tích tĩnh và kiểu]
Lint --> KiemThu{Kiểm thử đạt}
KiemThu -->|Không| CanhBao[Báo cho người commit]
KiemThu -->|Có| Build[Dựng image]
Build --> QuetBaoMat{Quét bảo mật sạch}
QuetBaoMat -->|Không| Dung["Dừng phát hành (rà soát thủ công)"]
QuetBaoMat -->|Có| TrienKhai[Triển khai lên môi trường thật]
TrienKhai --> KiemNhanh[Kiểm thử nhanh]
KiemNhanh --> Xong[Phát hành hoàn tất]Ví dụ có giải thích
1. Lưu đồ nhỏ nhất
Hai nút và một mũi tên. `TD` chạy từ trên xuống, `LR` từ trái sang phải; một sơ đồ rộng hơn cao thì hầu như luôn dễ đọc hơn với `LR`.
flowchart TD
NhanYeuCau[Nhận yêu cầu] --> GuiPhanHoi[Gửi phản hồi]2. Một nhánh rẽ có nhãn
Dấu ngoặc nhọn vẽ ra hình thoi. Văn bản giữa hai gạch đứng gắn nhãn cho cạnh chứ không phải cho nút. Sự phân biệt này sẽ quay lại ở phần lỗi bên dưới.
flowchart TD
BatDau[Nhận yêu cầu] --> XacThuc{Token hợp lệ}
XacThuc -->|Có| XuLy[Chạy trình xử lý]
XacThuc -->|Không| TuChoi[Trả về 401]
XuLy --> ThanhCong[Trả về 200]3. Để hình dạng nói lên điều gì đó
Hình dạng là cách rẻ nhất để thêm thông tin vào lưu đồ. Đầu bo tròn đánh dấu điểm bắt đầu và kết thúc, hình thoi là quyết định, hình trụ là nơi lưu dữ liệu.
flowchart LR
BatDau([Khởi động tác vụ]) --> Doc[(Đọc từ Postgres)]
Doc --> CoMoi{Có dòng mới}
CoMoi -->|Không| KetThuc([Kết thúc, không thay đổi])
CoMoi -->|Có| BienDoi[/Biến đổi dữ liệu/]
BienDoi --> Ghi[(Ghi lên S3)]
Ghi --> KetThuc4. Nhóm con để gom theo người chịu trách nhiệm
Nhóm con đặt các nút liên quan vào một khung. Nó phát huy tác dụng nhất khi gom theo bên chịu trách nhiệm chứ không theo giai đoạn: khi thấy rõ đội nào hay dịch vụ nào lo phần nào, các điểm bàn giao sẽ tự lộ ra.
flowchart TD
subgraph client [Trình duyệt]
UI[Gửi biểu mẫu]
end
subgraph api [Dịch vụ đơn hàng]
KiemTra[Kiểm tra dữ liệu nhập]
Luu[Lưu đơn hàng]
end
subgraph async [Tác vụ nền]
Mail[Gửi thư xác nhận]
HoaDon[Tạo hóa đơn]
end
UI --> KiemTra
KiemTra --> Luu
Luu --> Mail
Luu --> HoaDon5. Vòng lặp thử lại có giới hạn
Lưu đồ xử lý vòng lặp rất tốt. Vòng lặp thử lại chính là trường hợp chúng thật sự có ích, vì nhìn hình là thấy ngay vòng lặp có lối ra thật hay không.
flowchart TD
Gui[Gửi webhook] --> KetQua{Nhận được 2xx}
KetQua -->|Có| ThanhCong[Đánh dấu đã giao]
KetQua -->|Không| SoLan{Dưới 5 lần thử}
SoLan -->|Có| Cho[Chờ theo cấp số nhân]
Cho --> Gui
SoLan -->|Không| HangDoi[Đưa vào hàng đợi lỗi]Tóm tắt cú pháp lưu đồ
Mọi thứ ở đây chỉ dành riêng cho lưu đồ. Nhất là các mũi tên: chúng không dùng được ở loại sơ đồ khác. Dấu `->>` của sơ đồ tuần tự ở đây là lỗi cú pháp.
| Cú pháp | Ý nghĩa |
|---|---|
| flowchart TD | Từ trên xuống. `TB` cũng vậy. Hướng đọc thông thường của một quy trình. |
| flowchart LR | Từ trái sang phải. Còn có `RL`. Dùng cho luồng rộng và nông. |
| A[Văn bản] | Hình chữ nhật — một bước bình thường. |
| A(Văn bản) | Hình chữ nhật bo góc. |
| A([Văn bản]) | Hình sân vận động — theo quy ước là bắt đầu hoặc kết thúc. |
| A[(Văn bản)] | Hình trụ — nơi lưu dữ liệu. |
| A{Văn bản} | Hình thoi — quyết định. |
| A[/Văn bản/] | Hình bình hành — dữ liệu vào hoặc ra. |
| A --> B | Mũi tên. |
| A --- B | Đường thẳng không đầu mũi. |
| A -.-> B | Mũi tên nét đứt — theo quy ước là bất đồng bộ hoặc tùy chọn. |
| A ==> B | Mũi tên đậm — theo quy ước là đường chính. |
| A -->|văn bản| B | Cạnh có nhãn. Nếu có ngoặc đơn thì cần dấu nháy. |
| A["Văn bản (có ngoặc)"] | Nhãn trong dấu nháy — cần cho ngoặc đơn, dấu nháy và mọi ký tự bị đọc thành cú pháp hình dạng. |
| subgraph tên [Tiêu đề] ... end | Gom các nút vào một khung. Đóng bằng `end`. |
| %% chú thích | Dòng chú thích. Không được vẽ ra. |
Sáu lỗi thực sự làm hỏng lưu đồ
Tất cả đều được dựng lại trên chính bộ máy trang này dùng (Mermaid 11.12.2). Dán bản sai vào trình soạn thảo, bạn sẽ nhận đúng lỗi được mô tả; bản đã sửa thì vẽ được. Cách đọc lỗi Mermaid nhanh nhất là nhìn phần cuối: sau chữ `got` là token mà bộ phân tích bị kẹt lại.
Bạn thấy gì
Parse error, kết thúc bằng: got 'NODE_STRING'
Vì sao
Định danh nút có khoảng trắng — và đây là cái bẫy lớn nhất của tiếng Việt. Tiếng Việt viết rời từng âm tiết, nên gần như mọi danh từ tự nhiên đều có khoảng trắng: «đơn hàng», «thanh toán», «giao hàng». Định danh là token đứng trước mũi tên, và khoảng trắng cắt ngang nó, để lại một âm tiết không có chỗ đặt. Đã đo: `Đơn hàng --> Thanh toán` báo đúng lỗi này.
Cách sửa
Cho nút một định danh liền không khoảng trắng và đưa phần chữ dễ đọc vào nhãn. Dấu tiếng Việt hoàn toàn dùng được trong định danh — đã kiểm chứng: `ĐơnHàng` và `Đơn` chạy tốt. Chỉ khoảng trắng mới làm hỏng.
flowchart TD
Đơn hàng --> Thanh toánflowchart TD
DonHang[Đơn hàng mới] --> ThanhToan[Đã thanh toán]Bạn thấy gì
Parse error, kết thúc bằng: got 'PS'
Vì sao
Trong nhãn đặt giữa ngoặc vuông có một dấu ngoặc đơn mở. Ngoặc đơn là cú pháp hình dạng — `A(văn bản)` là nút bo tròn — nên một dấu ngoặc đơn trần bên trong ngoặc vuông bị đọc thành khởi đầu của một hình mới.
Cách sửa
Đặt cả nhãn trong dấu nháy kép. Bên trong dấu nháy, mọi thứ đều được coi là văn bản.
flowchart TD
A[Thử lại (tối đa 5 lần)] --> B[Xong]flowchart TD
A["Thử lại (tối đa 5 lần)"] --> B[Xong]Bạn thấy gì
Parse error, kết thúc bằng: got 'end'
Vì sao
Bạn đã dùng `end` làm định danh nút. Chữ `end` viết thường sẽ đóng một nhóm con, nên bộ phân tích thấy điểm kết thúc khối ở chỗ nó đang chờ một nút. Chuyện này xảy ra thường hơn ta tưởng: khi làm theo ví dụ tiếng Anh, nút cuối cùng hay bị đặt tên là `end` dù phần còn lại viết bằng tiếng Việt.
Cách sửa
Viết hoa chữ đó, hoặc cho nút một định danh khác và chuyển chữ ấy vào nhãn. `KetThuc` không gây vấn đề gì.
flowchart TD
BatDau[Bắt đầu] --> endflowchart TD
BatDau[Bắt đầu] --> KetThuc[Đã hoàn tất]Bạn thấy gì
Parse error trong nhãn cạnh nằm giữa hai gạch đứng
Vì sao
Trong nhãn cạnh có dấu ngoặc đơn. Văn bản giữa `|…|` chịu ràng buộc y hệt nhãn nút: ở đó ngoặc đơn cũng là cú pháp chứ không phải văn bản.
Cách sửa
Đặt cả nhãn cạnh trong dấu nháy.
flowchart TD
A -->|có (luôn luôn)| Bflowchart TD
A -->|"có (luôn luôn)"| BBạn thấy gì
Lexical error on line N. Unrecognized text. — mà định danh trông hoàn toàn đúng
Vì sao
Định danh được viết ở dạng Unicode tổ hợp (NFD) thay vì dạng dựng sẵn (NFC). Tiếng Việt có thể được lưu theo hai cách: `ơ` là một ký tự duy nhất (NFC), hoặc là `o` cộng thêm một dấu móc tổ hợp (NFD). Trên màn hình hai dạng này giống hệt nhau, không cách nào phân biệt bằng mắt. Đã đo: `ĐơnHàng` dạng NFC chạy tốt làm định danh nút, còn đúng chữ đó ở dạng NFD thì đổ với lỗi này. Ở vị trí nhãn thì cả hai dạng đều bình thường và cho ra viewBox y hệt nhau, nên lỗi chỉ lộ ra khi bạn dùng nó làm định danh. Đây là lỗi khó chẩn đoán nhất trên trang này: dán chữ tiếng Việt từ một nguồn lưu ở dạng NFD trông hoàn toàn chính xác và vẫn không phân tích được.
Cách sửa
Chuẩn hóa nguồn về NFC, hoặc đơn giản là gõ lại định danh bằng bàn phím của bạn thay vì dán vào — hầu hết bộ gõ tiếng Việt đều sinh ra NFC. Cách chắc chắn nhất là dùng định danh không dấu và để chữ có dấu trong nhãn, vì nhãn thì không bao giờ bị lỗi này.
flowchart TD
ĐơnHàng[Đơn hàng] --> B[Xong]flowchart TD
DonHang[Đơn hàng] --> B[Xong]Bạn thấy gì
Lexical error on line 1. Unrecognized text.
Vì sao
Hướng không hợp lệ. Lưu đồ chỉ chấp nhận TB, TD, BT, LR và RL; mọi thứ khác đều đổ ngay ở bước phân tích từ vựng, trước khi đọc được dù chỉ một nút. Vì vậy lỗi chỉ vào dòng 1 chứ không chỉ vào chỗ gõ nhầm.
Cách sửa
Dùng một trong năm hướng đó. TD và LR bao gần hết mọi trường hợp.
flowchart TRENXUONG
A --> Bflowchart TD
A --> BGhi chú về việc vẽ
Không có điều nào ở đây chép từ tài liệu: tất cả đều đo trên Mermaid 11.12.2, đúng phiên bản trang này dùng. Đây là những hành vi bắt đầu quan trọng khi sơ đồ không còn là đồ chơi.
Khoảng trắng giữa các âm tiết là ràng buộc lớn nhất của tiếng Việt
Không có ngôn ngữ nào khác trên trang này gặp chuyện này nặng bằng. Tiếng Việt viết rời từng âm tiết, nên một danh từ ba âm tiết đã có hai khoảng trắng, và mọi chỗ Mermaid chờ một định danh đều bị vướng. Trong lưu đồ thì lỗi này ồn ào: `Đơn hàng` báo Parse error ngay. Trong sơ đồ trạng thái thì nó im lặng, và tệ hơn nhiều — đã đo, `Chờ thanh toán` biến thành ba trạng thái riêng biệt là `Chờ`, `thanh` và `toán`. Tin tốt là bản thân dấu tiếng Việt không hề có vấn đề: `ĐơnHàng` chạy tốt làm định danh. Cách làm là viết liền các âm tiết trong định danh và để nguyên khoảng trắng trong nhãn.
Nhãn chỉ xuống dòng ở khoảng trắng
Đã đo: nhãn nút nở ra đến giới hạn 276 pixel của viewBox, rồi xuống dòng và cao thêm khoảng 24 pixel mỗi dòng. Với tiếng Việt điều này lại hóa thuận lợi, vì âm tiết nào cũng ngắn và cách nhau bằng khoảng trắng, nên chỗ ngắt dòng lúc nào cũng sẵn có. Đây là mặt bù lại của chính đặc điểm gây phiền ở phần định danh: tiếng Việt gần như không bao giờ gặp cảnh một từ dài phá vỡ khung như tiếng Hà Lan hay tiếng Đức. Về bề rộng thì cùng một ý nghĩa với `A[Payment received] --> B[Ship the order]` đo được 204 pixel, bản tiếng Việt ra 217 pixel — dấu thanh nằm bên trên chữ cái nên không làm chữ rộng thêm.
Hai cách lưu chữ tiếng Việt trông giống hệt nhau, chỉ một cách dùng được làm định danh
Đây là điều đáng ngạc nhiên nhất mà tôi đo được về tiếng Việt. Unicode cho phép lưu `ơ` theo hai cách: một ký tự dựng sẵn (NFC), hoặc `o` cộng một dấu móc tổ hợp (NFD). Trên màn hình chúng không khác nhau một chút nào, và ở vị trí nhãn thì cả hai đều vẽ ra bình thường với viewBox y hệt nhau — đã đo, cùng ra 249,3 pixel. Nhưng ở vị trí định danh thì chỉ NFC chạy được: `ĐơnHàng` dạng NFD đổ với `Lexical error on line N. Unrecognized text.` Nghĩa là bạn có thể dán một định danh trông hoàn toàn chính xác và nhận về một lỗi không có nguyên nhân nhìn thấy được. Nếu một định danh tiếng Việt bị báo lỗi mà bạn không hiểu tại sao, hãy gõ lại nó bằng tay thay vì dán, hoặc chuyển sang định danh không dấu.
Chiều cao tăng khoảng 105 pixel mỗi nút, chiều rộng gần như đứng yên
Trong sơ đồ từ trên xuống, ba nút cho viewBox khoảng 126×278. Đến bốn mươi nút thì thành 135×4126: chiều rộng chỉ tăng 9 pixel còn chiều cao gấp mười lăm lần. Một sơ đồ dài là một dải hẹp không màn hình nào chứa nổi. Nút canh giữa trong khung xem trước sinh ra vì lý do đó. Khi nó dài quá mức, chuyển sang `flowchart LR` thường làm tỷ lệ hai cạnh giảm gần một nửa.
Xuất PNG từng hỏng vì nhãn là HTML
Nhãn của lưu đồ được vẽ dưới dạng HTML thật bên trong một `<foreignObject>` trong SVG. Đó là lý do `<br>` và một ít Markdown dùng được trong nhãn. Cũng vì thế trình duyệt từ chối vẽ SVG này lên canvas: chức năng xuất PNG của trang này đã lặng lẽ trả về tệp SVG trong một thời gian dài. Bây giờ trước khi xuất, sơ đồ được vẽ lại bằng nhãn văn bản SVG thuần và tệp PNG ra đúng. Cái giá phải trả là kiểu chữ trong PNG xuất ra khác rất nhẹ so với trên màn hình.
Khi nào nên dùng loại sơ đồ khác
Nếu vấn đề là ai gửi gì cho ai và thứ tự thời gian nặng ký hơn các nhánh rẽ, sơ đồ tuần tự đọc dễ hơn và vẫn giữ được sự dễ đọc khi nó lớn dần. Một lưu đồ có sáu bên tham gia được viết thành tên nút chính là một sơ đồ tuần tự chưa chịu thừa nhận điều đó.
Nếu bạn không mô tả một thủ tục mà mô tả các trạng thái mà một thứ có thể ở trong đó, hãy dùng sơ đồ trạng thái. Phân biệt rất dễ: nếu nhãn nút là trạng thái như «đơn hàng chờ xử lý» hay «đơn hàng đã gửi» thì đó là máy trạng thái; nếu là hành động như «kiểm tra dữ liệu nhập» hay «gửi thư» thì đó là lưu đồ.
Còn vượt quá bốn mươi nút thì nói thật, không sơ đồ nào cứu được. Hoặc bạn tách nó thành nhiều sơ đồ dùng chung một điểm vào, hoặc bạn chấp nhận rằng điều mình muốn kể quá phức tạp để nằm gọn trong một bức hình. Bản thân điều đó cũng là một thông tin hữu ích.
Các loại sơ đồ khác
Viết bởi Dominik Malsch · Cập nhật lần cuối: