Miễn phí · Không cần đăng ký · Hỗ trợ tệp .mmd

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ả
Mở cái này trong trình soạn thảo
Quảng cáo

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 --> [*]
Mở trong trình soạn thảo

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 --> [*]
Mở trong trình soạn thảo

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 --> [*]
Mở trong trình soạn thảo

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 --> [*]
Mở trong trình soạn thảo

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 --> [*]
Mở trong trình soạn thảo

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-v2Mở sơ đồ. `stateDiagram` vẫn chạy nhưng dùng bố cục cũ.
[*] --> ATrạng thái khởi đầu — điểm vào.
A --> [*]Trạng thái kết thúc.
A --> BMột chuyển tiếp không tên.
A --> B: sự kiệnChuyển tiếp gắn nhãn bằng sự kiện gây ra nó.
state "Nhãn" as idNhã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ảnThêm ghi chú. Còn có `note left of`.
direction LRXếp máy trạng thái từ trái sang phải thay vì từ trên xuống.
Quảng cáo

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ệ.

Sai
stateDiagram-v2
    [*] --> Chờ thanh toán
    Chờ thanh toán --> Đã hủy
Đúng
stateDiagram-v2
    state "Chờ thanh toán" as choThanhToan
    [*] --> choThanhToan
    choThanhToan --> DaHuy

Bạ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.

Sai
stateDiagram-v2
    [*] --> dang-xu-ly
    dang-xu-ly --> DaDong
Đúng
stateDiagram-v2
    state "Đang xử lý" as dangXuLy
    [*] --> dangXuLy
    dangXuLy --> DaDong

Bạ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.

Sai
stateDiagram-v2
    [*] --> Ngoai
    state Ngoai {
        [*] --> Trong
Đúng
stateDiagram-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 `--`.

Sai
stateDiagram-v2
    state CaHai {
        [*] --> A
        ---
        [*] --> B
    }
Đúng
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`.

Sai
stateDiagram-v3
    [*] --> Nhap
Đúng
stateDiagram-v2
    [*] --> Nhap

Bạ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.

Sai
stateDiagram-v2
    [*] --> kiemTra
    kiemTra --> DatChuan
    kiemTra --> LoiHang
    state kiemTra <<choice>>
Đúng
stateDiagram-v2
    state kiemTra <<choice>>
    [*] --> kiemTra
    kiemTra --> DatChuan
    kiemTra --> LoiHang

Ghi 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:

Mở trình soạn thảo →