Trình tạo sơ đồ trạng thái Mermaid
Sơ đồ trạng thái cho thấy một thứ có thể ở những trạng thái nào và cái gì đưa nó từ trạng thái này sang trạng thái khác. Nó hợp khi đối tượng có vòng đời — một đơn hàng, một hóa đơn, một gói thuê bao — và câu hỏi đáng quan tâm là những chuyển tiếp nào được phép. Nếu bạn mô tả các bước phải làm chứ không phải các trạng thái để ở trong, bạn cần lưu đồ.
Vòng đời của một đơn hàng bán lẻ trực tuyến
Đây là trường hợp máy trạng thái xứng đáng có chỗ đứng: sau khi giao hàng có ba kết cục loại trừ nhau và hai trong số đó quay ngược lại. Hãy để ý mọi định danh trạng thái đều được khai báo bằng `state "Nhãn" as id`. Trong tiếng Việt đó không phải kiểu cách mà là điều bắt buộc, vì lý do giải thích ở phần lỗi bên dưới.
stateDiagram-v2
direction LR
state "Nháp" as nhap
state "Chờ thanh toán" as choThanhToan
state "Sẵn sàng giao" as sanSangGiao
state "Đang giao" as dangGiao
state "Đã giao" as daGiao
state "Yêu cầu trả hàng" as traHang
[*] --> nhap
nhap --> choThanhToan: đã đặt hàng
choThanhToan --> sanSangGiao: nhận được tiền
choThanhToan --> [*]: quá hạn sau 24 giờ
sanSangGiao --> dangGiao: bên vận chuyển đã lấy hàng
dangGiao --> daGiao: người nhận đã ký
daGiao --> traHang: trong thời hạn đổi trả
traHang --> choThanhToan: đã hoàn tiền
daGiao --> [*]: hết thời hạn đổi trảVí dụ có giải thích
1. Máy trạng thái nhỏ nhất
`[*]` vừa là điểm vào vừa là điểm ra: nó là cái nào thì tùy vào nó nằm ở phía nào của mũi tên. Đây là bộ khung tối thiểu đáng để vẽ.
stateDiagram-v2
[*] --> HoatDong
HoatDong --> DaDong
DaDong --> [*]2. Chuyển tiếp có tên
Văn bản sau dấu hai chấm gọi tên sự kiện gây ra chuyển tiếp. Đó là phần làm sơ đồ trở nên hữu ích: không có nó, bạn chỉ có một danh sách trạng thái và không có luật lệ nào.
stateDiagram-v2
[*] --> ChuaThanhToan
ChuaThanhToan --> DaThanhToan: thu tiền thành công
ChuaThanhToan --> HetHan: quá thời hạn
DaThanhToan --> DaGui: đã xuất kho
DaGui --> DaNhan: người nhận đã ký
DaNhan --> [*]
HetHan --> [*]3. Nhãn dễ đọc với định danh an toàn
Trong tiếng Việt gần như không tên trạng thái nào chỉ có một âm tiết, và khoảng trắng trong định danh làm hỏng mọi thứ trong im lặng. `state "Nhãn" as id` giải quyết dứt điểm: nhãn thì dễ đọc, định danh thì an toàn.
stateDiagram-v2
state "Chờ thanh toán" as choThanhToan
state "Sẵn sàng giao hàng" as sanSangGiao
state "Đang trên đường" as dangTrenDuong
[*] --> choThanhToan
choThanhToan --> sanSangGiao: đã xác nhận thanh toán
sanSangGiao --> dangTrenDuong: bên vận chuyển đã lấy hàng
dangTrenDuong --> [*]4. Trạng thái ghép và điểm lựa chọn
Trạng thái ghép chứa một máy trạng thái riêng bên trong: dùng nó khi một giai đoạn có diễn biến nội bộ đáng kể. `<<choice>>` là điểm rẽ nhánh có điều kiện, và phải khai báo trước các chuyển tiếp dùng đến nó.
stateDiagram-v2
state kiemTra <<choice>>
[*] --> DangXuLy
state DangXuLy {
[*] --> NhatHang
NhatHang --> DongGoi: đã nhặt đủ hàng
DongGoi --> [*]
}
DangXuLy --> kiemTra
kiemTra --> DatChuan: qua kiểm tra chất lượng
kiemTra --> LoiHang: phát hiện lỗi
DatChuan --> [*]
LoiHang --> [*]5. Các vùng chạy song song
Hai dấu gạch ngang trên một dòng riêng chia một trạng thái ghép thành các vùng cùng hoạt động một lúc. Đây là điều duy nhất sơ đồ trạng thái làm được mà lưu đồ thật sự không làm nổi.
stateDiagram-v2
[*] --> DangKy
state DangKy {
[*] --> ChuaXacMinhEmail
ChuaXacMinhEmail --> DaXacMinhEmail: đã bấm liên kết
--
[*] --> HoSoTrong
HoSoTrong --> HoSoDayDu: đã gửi biểu mẫu
}
DangKy --> HoatDong: cả hai đã xong
HoatDong --> [*]Tóm tắt cú pháp sơ đồ trạng thái
Hãy dùng `stateDiagram-v2` chứ không phải `stateDiagram`. Cả hai đều vẽ được, nhưng v2 là bộ máy bố cục vẫn đang được phát triển và xử lý trạng thái ghép, trạng thái song song tốt hơn thấy rõ.
| Cú pháp | Ý nghĩa |
|---|---|
| stateDiagram-v2 | Mở sơ đồ. `stateDiagram` vẫn chạy nhưng dùng bố cục cũ. |
| [*] --> A | Trạng thái khởi đầu — điểm vào. |
| A --> [*] | Trạng thái kết thúc. |
| A --> B | Một chuyển tiếp không tên. |
| A --> B: sự kiện | Chuyển tiếp gắn nhãn bằng sự kiện gây ra nó. |
| state "Nhãn" as id | Nhãn dễ đọc kèm định danh không có khoảng trắng. |
| state A { ... } | Trạng thái ghép, bên trong có máy trạng thái riêng. |
| -- | Bên trong trạng thái ghép, nó chia trạng thái đó thành các vùng song song. |
| state x <<choice>> | Điểm rẽ nhánh có điều kiện. |
| state f <<fork>> / <<join>> | Tách thành các chuyển tiếp song song rồi gộp lại. |
| note right of A: văn bản | Thêm ghi chú. Còn có `note left of`. |
| direction LR | Xếp máy trạng thái từ trái sang phải thay vì từ trên xuống. |
Sáu lỗi làm hỏng sơ đồ trạng thái
Dựng lại trên Mermaid 11.12.2. Bốn lỗi đầu chặn việc vẽ. Hai lỗi cuối tệ hơn: chúng vẽ ra tỉnh bơ và trả về một sơ đồ không mang ý nghĩa bạn đã viết. Lỗi đầu tiên là lỗi đắt giá nhất với người viết tiếng Việt trên toàn bộ trang này.
Bạn thấy gì
Vẽ được, nhưng một trạng thái biến thành nhiều ô
Vì sao
Khoảng trắng trong định danh trạng thái. Đây là lỗi tốn kém nhất với tiếng Việt, vì tiếng Việt viết rời từng âm tiết nên một tên trạng thái ba âm tiết đã có hai khoảng trắng. Mermaid không từ chối, và cũng không coi phần sau là mô tả: nó tạo một ô riêng cho mỗi âm tiết. Đã đo bằng cách đọc các định danh sinh ra — `[*] --> Chờ thanh toán` cho ra ba trạng thái riêng biệt là `Chờ`, `thanh` và `toán`, và chỉ cái đầu tiên nối vào mũi tên; hai cái còn lại nằm trơ ra không nối vào đâu. Sơ đồ rộng ra 341 pixel mà không nói một lời. Cơ chế mô tả có thật, nhưng nó đòi dấu hai chấm: `choThanhToan: đang chờ khách chuyển tiền`.
Cách sửa
Khai báo trạng thái bằng `state "Nhãn" as id` và luôn tham chiếu tới nó qua định danh. Trong tiếng Việt hãy coi đây là quy tắc mặc định, không phải trường hợp ngoại lệ.
stateDiagram-v2
[*] --> Chờ thanh toán
Chờ thanh toán --> Đã hủystateDiagram-v2
state "Chờ thanh toán" as choThanhToan
[*] --> choThanhToan
choThanhToan --> DaHuyBạn thấy gì
Parse error, kết thúc bằng: got 'INVALID'
Vì sao
Dấu gạch ngang trong định danh trạng thái. Tên có gạch ngang rất dễ nghĩ ra khi muốn tránh khoảng trắng — `dang-xu-ly`, `cho-thanh-toan` — nhưng dấu gạch ngang bị đọc thành phần đầu của một mũi tên chuyển tiếp.
Cách sửa
Dùng chữ viết liền hoặc dấu gạch dưới cho định danh, và đặt chữ dễ đọc vào nhãn trong dấu nháy.
stateDiagram-v2
[*] --> dang-xu-ly
dang-xu-ly --> DaDongstateDiagram-v2
state "Đang xử lý" as dangXuLy
[*] --> dangXuLy
dangXuLy --> DaDongBạn thấy gì
Parse error bên trong một trạng thái ghép
Vì sao
Một trạng thái ghép mở bằng `{` mà không bao giờ đóng. Dấu ngoặc nhọn đóng phải nằm trên một dòng riêng.
Cách sửa
Đóng khối lại.
stateDiagram-v2
[*] --> Ngoai
state Ngoai {
[*] --> TrongstateDiagram-v2
[*] --> Ngoai
state Ngoai {
[*] --> Trong
}Bạn thấy gì
Lexical error on line N. Unrecognized text.
Vì sao
Dấu phân cách vùng song song viết sai số lượng gạch ngang. Bên trong một trạng thái ghép, trên một dòng riêng, nó đúng bằng hai. Ba gạch ngang là một token hoàn toàn khác.
Cách sửa
Dùng đúng `--`.
stateDiagram-v2
state CaHai {
[*] --> A
---
[*] --> B
}stateDiagram-v2
state CaHai {
[*] --> A
--
[*] --> B
}Bạn thấy gì
Parse error on line 1, kết thúc bằng: got 'ID'
Vì sao
Hậu tố phiên bản không tồn tại. Chỉ có `stateDiagram` và `stateDiagram-v2`, không có gì khác: `-v3` đổ ngay ở dòng đầu.
Cách sửa
Dùng `stateDiagram-v2`.
stateDiagram-v3
[*] --> NhapstateDiagram-v2
[*] --> NhapBạn thấy gì
Vẽ được, nhưng điểm lựa chọn lại được vẽ như một trạng thái bình thường
Vì sao
Khai báo `<<choice>>` đặt sau các chuyển tiếp dùng đến nó. Mermaid tạo trạng thái ngay lần đầu gặp nó, và một stereotype thêm vào sau không làm thay đổi cái đã được tạo.
Cách sửa
Khai báo các trạng thái giả trước những chuyển tiếp tham chiếu tới chúng.
stateDiagram-v2
[*] --> kiemTra
kiemTra --> DatChuan
kiemTra --> LoiHang
state kiemTra <<choice>>stateDiagram-v2
state kiemTra <<choice>>
[*] --> kiemTra
kiemTra --> DatChuan
kiemTra --> LoiHangGhi chú về việc vẽ
Đo trên Mermaid 11.12.2, đúng phiên bản trang này dùng.
Đây là chỗ tiếng Việt bị phạt nặng nhất, và hình phạt thì hoàn toàn im lặng
So sánh giữa các loại sơ đồ rất đáng biết, vì cùng một sơ suất bị xử lý rất khác nhau. Trong lưu đồ, khoảng trắng trong định danh nút cho Parse error và bạn biết ngay. Trong sơ đồ trạng thái thì không có gì cả: sơ đồ vẫn vẽ, và mỗi âm tiết thành một ô. Đã đo: `Chờ thanh toán` cho ba ô, `Chờ`, `thanh` và `toán`, với viewBox rộng 341 pixel thay vì hai ô như bạn định. Trong các thứ tiếng viết dính từ, một tên hai từ chỉ vỡ thành hai; tiếng Việt vỡ theo từng âm tiết, nên đây là ngôn ngữ chịu thiệt nặng nhất trong số các ngôn ngữ đo được trên trang này. Cách phòng vệ duy nhất là viết `state "…" as id` thành thói quen.
Chiều cao tăng khoảng 114 pixel mỗi trạng thái
Đã đo: ba trạng thái cho viewBox khoảng 91×348, bốn mươi trạng thái cho 100×4566 — chừng 114 pixel mỗi trạng thái. Giống lưu đồ, chiều rộng gần như không nhúc nhích: máy trạng thái lớn dần xuống dưới. Nếu vòng đời dài mà ít rẽ nhánh, đặt `direction LR` vào sơ đồ là cách xử lý quen thuộc.
Cả stateDiagram lẫn stateDiagram-v2 đều vẽ được — và đó chính là cái bẫy
Người ta hay đọc thấy rằng «không dùng `stateDiagram-v2` thì chẳng vẽ được gì». Ở bản 11.12.2 điều đó không đúng: cả hai từ khóa đều vẽ ra không lỗi. Thứ khác nhau là chất lượng bố cục, nhất là với trạng thái ghép và trạng thái song song, và khi dùng từ khóa cũ thì không có cảnh báo nào hiện ra. Nếu một trạng thái ghép trông chật chội hoặc các mũi tên đi vòng vèo lạ lùng, hãy xem bạn đã mở sơ đồ bằng từ khóa nào trước khi bắt tay viết lại.
Nhãn là HTML, nên việc xuất PNG phải vẽ lại
Giống lưu đồ và các sơ đồ lớp, ER, nhãn trạng thái cũng được vẽ bên trong một `<foreignObject>` trong SVG. Vì trình duyệt từ chối chuyển thứ đó thành ảnh raster trên canvas, chức năng xuất PNG của trang này vẽ lại sơ đồ trước bằng nhãn văn bản SVG thuần. Tệp PNG ra đúng và đủ kích thước; kiểu chữ khác rất nhẹ so với trên màn hình.
Chủ đề đổi màu, không bao giờ đổi hình học
Chủ đề sáng và tối cho ra cùng một viewBox với cùng một mã nguồn, nên máy trạng thái không bị sắp xếp lại khi đổi chủ đề.
Khi nào nên dùng loại sơ đồ khác
Nếu nhãn của bạn là hành động — kiểm tra, gửi đi, thử lại — thì bạn đang mô tả một quy trình chứ không phải một vòng đời, và lựa chọn trung thực là lưu đồ. Dấu hiệu rõ nhất là bạn không trả lời được câu hỏi «cái đang ở trạng thái này là cái gì?».
Nếu nhiều thành phần mỗi cái có vòng đời riêng và điều đáng quan tâm lại là sự tương tác giữa chúng, thì một sơ đồ trạng thái cho mỗi thành phần cộng với một sơ đồ tuần tự cho phần tương tác sẽ hơn hẳn một cỗ máy khổng lồ duy nhất.
Nếu trạng thái nào cũng nối với mọi trạng thái khác, sơ đồ sẽ thành một mớ rối dù bạn vẽ thế nào. Thường thì điều đó có nghĩa những thứ bạn liệt kê ra như trạng thái thật ra là các cờ kết hợp tự do với nhau; khi ấy một bảng liệt kê các tổ hợp hợp lệ nói được nhiều hơn một bức hình.
Các loại sơ đồ khác
Viết bởi Dominik Malsch · Cập nhật lần cuối: