ตัวแก้ไขไดอะแกรมสถานะ Mermaid
ไดอะแกรมสถานะแสดงว่าสิ่งหนึ่งอาจอยู่ในสถานะใดได้บ้าง และอะไรพามันจากสถานะหนึ่งไปอีกสถานะหนึ่ง เหมาะเมื่อสิ่งนั้นมีวงจรชีวิต เช่น คำสั่งซื้อ ใบกำกับภาษี หรือการเป็นสมาชิก และคำถามที่น่าสนใจคือการเปลี่ยนสถานะแบบใดที่อนุญาต ถ้าคุณกำลังอธิบายขั้นตอนที่ต้องทำ ไม่ใช่สถานะที่จะอยู่ สิ่งที่คุณต้องการคือผังงาน
วงจรชีวิตของคำสั่งซื้อออนไลน์
นี่คือกรณีที่เครื่องสถานะคุ้มค่ากับที่ทางของมัน หลังจัดส่งแล้วมีปลายทางสามแบบที่ตัดกันเอง และสองในนั้นย้อนกลับ สังเกตว่าตัวระบุของสถานะเป็นอักษรไทยได้ตรง ๆ เพราะไดอะแกรมสถานะยอมรับอักษรไทยในตำแหน่งตัวระบุ ต่างจากผังงาน
stateDiagram-v2
direction LR
[*] --> ฉบับร่าง
ฉบับร่าง --> รอชำระเงิน: สั่งซื้อแล้ว
รอชำระเงิน --> พร้อมจัดส่ง: ได้รับเงินแล้ว
รอชำระเงิน --> [*]: หมดอายุใน 24 ชั่วโมง
พร้อมจัดส่ง --> กำลังจัดส่ง: ผู้ขนส่งรับของแล้ว
กำลังจัดส่ง --> ส่งถึงแล้ว: ผู้รับเซ็นรับ
ส่งถึงแล้ว --> ขอคืนสินค้า: อยู่ในกำหนดคืนสินค้า
ขอคืนสินค้า --> รอชำระเงิน: คืนเงินแล้ว
ส่งถึงแล้ว --> [*]: พ้นกำหนดคืนสินค้าตัวอย่างพร้อมคำอธิบาย
1. เครื่องสถานะที่เล็กที่สุด
`[*]` เป็นทั้งจุดเข้าและจุดออก จะเป็นอันไหนขึ้นอยู่กับว่ามันอยู่ข้างใดของลูกศร นี่คือโครงที่เล็กที่สุดที่คุ้มค่าจะวาด
stateDiagram-v2
[*] --> ใช้งานอยู่
ใช้งานอยู่ --> ปิดแล้ว
ปิดแล้ว --> [*]2. การเปลี่ยนสถานะที่มีชื่อ
ข้อความหลังทวิภาคระบุเหตุการณ์ที่ทำให้เกิดการเปลี่ยนสถานะ นี่คือส่วนที่ทำให้ไดอะแกรมมีประโยชน์ เพราะถ้าไม่มีมัน คุณก็มีแค่รายชื่อสถานะโดยไม่มีกฎอะไรเลย
stateDiagram-v2
[*] --> ยังไม่ชำระ
ยังไม่ชำระ --> ชำระแล้ว: เก็บเงินสำเร็จ
ยังไม่ชำระ --> หมดอายุ: เลยกำหนด
ชำระแล้ว --> จัดส่งแล้ว: ออกจากคลัง
จัดส่งแล้ว --> รับแล้ว: ผู้รับเซ็นรับ
รับแล้ว --> [*]
หมดอายุ --> [*]3. ป้ายกำกับที่อ่านง่ายกับตัวระบุที่ปลอดภัย
ถึงอักษรไทยจะใช้เป็นตัวระบุสถานะได้ แต่ถ้าชื่อสถานะมีช่องว่างคั่นวลีหรือมีตัวเลขปนอยู่ มันจะแตกออกอย่างเงียบ ๆ `state "ป้ายกำกับ" as ตัวระบุ` ปิดความเสี่ยงนั้นได้ในครั้งเดียว
stateDiagram-v2
state "รอชำระเงิน ภายใน 24 ชั่วโมง" as waitPay
state "พร้อมจัดส่ง" as readyShip
state "กำลังเดินทางไปหาลูกค้า" as onTheWay
[*] --> waitPay
waitPay --> readyShip: ยืนยันการชำระเงินแล้ว
readyShip --> onTheWay: ผู้ขนส่งรับของแล้ว
onTheWay --> [*]4. สถานะประกอบและจุดตัดสินใจ
สถานะประกอบมีเครื่องสถานะของตัวเองอยู่ข้างใน ใช้เมื่อขั้นตอนหนึ่งมีชีวิตภายในที่สำคัญ ส่วน `<<choice>>` คือจุดแตกแขนงตามเงื่อนไข และต้องประกาศก่อนการเปลี่ยนสถานะที่ใช้มัน
stateDiagram-v2
state ตรวจสอบ <<choice>>
[*] --> กำลังดำเนินการ
state กำลังดำเนินการ {
[*] --> หยิบสินค้า
หยิบสินค้า --> บรรจุหีบห่อ: หยิบครบแล้ว
บรรจุหีบห่อ --> [*]
}
กำลังดำเนินการ --> ตรวจสอบ
ตรวจสอบ --> ผ่าน: ผ่านการควบคุมคุณภาพ
ตรวจสอบ --> ไม่ผ่าน: พบข้อบกพร่อง
ผ่าน --> [*]
ไม่ผ่าน --> [*]5. พื้นที่ที่ทำงานคู่ขนาน
ขีดกลางสองขีดบนบรรทัดของตัวเองแบ่งสถานะประกอบออกเป็นพื้นที่ที่ทำงานพร้อมกัน นี่คือสิ่งเดียวที่ไดอะแกรมสถานะทำได้และผังงานทำไม่ได้จริง ๆ
stateDiagram-v2
[*] --> การสมัคร
state การสมัคร {
[*] --> ยังไม่ยืนยันอีเมล
ยังไม่ยืนยันอีเมล --> ยืนยันอีเมลแล้ว: คลิกลิงก์แล้ว
--
[*] --> โปรไฟล์ว่าง
โปรไฟล์ว่าง --> โปรไฟล์ครบ: ส่งแบบฟอร์มแล้ว
}
การสมัคร --> ใช้งานได้: ครบทั้งสองอย่าง
ใช้งานได้ --> [*]สรุปไวยากรณ์ไดอะแกรมสถานะ
ใช้ `stateDiagram-v2` ไม่ใช่ `stateDiagram` ทั้งคู่แสดงผลได้ แต่ v2 คือเอนจินจัดวางที่ยังพัฒนาอยู่ และรับมือกับสถานะประกอบและสถานะคู่ขนานได้ดีกว่าอย่างเห็นได้ชัด
| ไวยากรณ์ | ความหมาย |
|---|---|
| stateDiagram-v2 | เปิดไดอะแกรม `stateDiagram` ยังใช้ได้แต่ใช้การจัดวางแบบเก่า |
| [*] --> A | สถานะเริ่มต้น — จุดเข้า |
| A --> [*] | สถานะสุดท้าย |
| A --> B | การเปลี่ยนสถานะที่ไม่มีชื่อ |
| A --> B: เหตุการณ์ | การเปลี่ยนสถานะที่กำกับด้วยเหตุการณ์ที่ทำให้เกิด |
| state "ป้ายกำกับ" as ตัวระบุ | ป้ายกำกับที่อ่านง่ายพร้อมตัวระบุที่ไม่มีช่องว่าง |
| state A { ... } | สถานะประกอบ ที่มีเครื่องสถานะของตัวเองอยู่ข้างใน |
| -- | ภายในสถานะประกอบ ใช้แบ่งเป็นพื้นที่คู่ขนาน |
| state x <<choice>> | จุดแตกแขนงตามเงื่อนไข |
| state f <<fork>> / <<join>> | แยกเป็นการเปลี่ยนสถานะคู่ขนานแล้วรวมกลับ |
| note right of A: ข้อความ | เพิ่มหมายเหตุ มี `note left of` ด้วย |
| direction LR | จัดวางเครื่องสถานะจากซ้ายไปขวาแทนบนลงล่าง |
หกข้อผิดพลาดที่ทำให้ไดอะแกรมสถานะพัง
สร้างซ้ำบน Mermaid 11.12.2 สี่ข้อแรกหยุดการแสดงผล สองข้อสุดท้ายแย่กว่านั้น เพราะมันแสดงผลออกมาหน้าตาเฉยและคืนไดอะแกรมที่ไม่ได้มีความหมายอย่างที่คุณเขียน
สิ่งที่คุณเห็น
แสดงผลได้ แต่สถานะเดียวกลายเป็นหลายกล่อง
ทำไม
มีช่องว่างอยู่ในตัวระบุสถานะ ภาษาไทยเจอเรื่องนี้น้อยกว่าภาษาอื่นมาก เพราะเราไม่เว้นวรรคระหว่างคำ แต่ยังเกิดได้อยู่ดี เพราะเราเว้นวรรคคั่นวลีและเว้นรอบตัวเลขกับอักษรละติน Mermaid ไม่ปฏิเสธและไม่ถือว่าส่วนที่เหลือเป็นคำอธิบาย มันสร้างกล่องแยกให้ทุกชิ้นที่คั่นด้วยช่องว่าง โดยมีเพียงชิ้นแรกที่ต่อกับลูกศร ส่วนกลไกคำอธิบายมีอยู่จริงแต่ต้องใช้ทวิภาค เช่น `waitPay: กำลังรอเงินโอนเข้า`
วิธีแก้
ประกาศสถานะด้วย `state "ป้ายกำกับ" as ตัวระบุ` แล้วอ้างถึงมันด้วยตัวระบุเสมอ โดยเฉพาะเมื่อชื่อสถานะมีตัวเลขหรืออักษรละตินปนอยู่
stateDiagram-v2
[*] --> รอชำระ 24 ชั่วโมง
รอชำระ 24 ชั่วโมง --> ปิดแล้วstateDiagram-v2
state "รอชำระ 24 ชั่วโมง" as waitPay
[*] --> waitPay
waitPay --> ปิดแล้วสิ่งที่คุณเห็น
Parse error ที่ลงท้ายด้วย: got 'INVALID'
ทำไม
มีขีดกลางอยู่ในตัวระบุสถานะ ชื่อที่มีขีดกลางเป็นสิ่งที่นึกออกได้เองเมื่อพยายามเลี่ยงช่องว่าง แต่ขีดกลางถูกอ่านว่าเป็นจุดเริ่มของลูกศรเปลี่ยนสถานะ
วิธีแก้
ใช้คำเดียวหรือขีดล่างสำหรับตัวระบุ แล้วใส่ข้อความที่อ่านได้ไว้ในป้ายกำกับที่อยู่ในเครื่องหมายคำพูด
stateDiagram-v2
[*] --> in-progress
in-progress --> ปิดแล้วstateDiagram-v2
state "กำลังดำเนินการ" as inProgress
[*] --> inProgress
inProgress --> ปิดแล้วสิ่งที่คุณเห็น
Parse error ภายในสถานะประกอบ
ทำไม
สถานะประกอบที่เปิดด้วย `{` แล้วไม่เคยปิด วงเล็บปีกกาปิดต้องอยู่บนบรรทัดของตัวเอง
วิธีแก้
ปิดบล็อกให้เรียบร้อย
stateDiagram-v2
[*] --> ภายนอก
state ภายนอก {
[*] --> ภายในstateDiagram-v2
[*] --> ภายนอก
state ภายนอก {
[*] --> ภายใน
}สิ่งที่คุณเห็น
Lexical error on line N. Unrecognized text.
ทำไม
ตัวแบ่งพื้นที่คู่ขนานเขียนด้วยจำนวนขีดกลางที่ผิด ภายในสถานะประกอบและบนบรรทัดของตัวเอง มันต้องเป็นสองขีดพอดี สามขีดเป็นโทเคนคนละตัวไปเลย
วิธีแก้
ใช้ `--` เป๊ะ ๆ
stateDiagram-v2
state ทั้งสอง {
[*] --> A
---
[*] --> B
}stateDiagram-v2
state ทั้งสอง {
[*] --> A
--
[*] --> B
}สิ่งที่คุณเห็น
Parse error on line 1 ที่ลงท้ายด้วย: got 'ID'
ทำไม
ส่วนต่อท้ายรุ่นที่ไม่มีอยู่จริง มีแค่ `stateDiagram` กับ `stateDiagram-v2` เท่านั้น ไม่มีอย่างอื่น `-v3` ล้มตั้งแต่บรรทัดแรก
วิธีแก้
ใช้ `stateDiagram-v2`
stateDiagram-v3
[*] --> ฉบับร่างstateDiagram-v2
[*] --> ฉบับร่างสิ่งที่คุณเห็น
แสดงผลได้ แต่จุดตัดสินใจถูกวาดเป็นสถานะธรรมดา
ทำไม
การประกาศ `<<choice>>` มาหลังการเปลี่ยนสถานะที่ใช้มัน Mermaid สร้างสถานะขึ้นทันทีที่เจอครั้งแรก และสเตอริโอไทป์ที่เติมทีหลังไม่เปลี่ยนสิ่งที่สร้างไปแล้ว
วิธีแก้
ประกาศสถานะเทียมก่อนการเปลี่ยนสถานะที่อ้างถึงมัน
stateDiagram-v2
[*] --> ตรวจสอบ
ตรวจสอบ --> ผ่าน
ตรวจสอบ --> ไม่ผ่าน
state ตรวจสอบ <<choice>>stateDiagram-v2
state ตรวจสอบ <<choice>>
[*] --> ตรวจสอบ
ตรวจสอบ --> ผ่าน
ตรวจสอบ --> ไม่ผ่านหมายเหตุเรื่องการแสดงผล
วัดบน Mermaid 11.12.2 ซึ่งเป็นรุ่นที่เว็บนี้ใช้
ที่นี่อักษรไทยใช้เป็นตัวระบุได้ ต่างจากผังงาน
ข้อแตกต่างนี้ควรรู้เพราะใช้ประโยชน์ได้จริง อักษรไทยในตัวระบุโหนดของผังงานและในชื่อคลาสให้ `Lexical error` แต่ในตัวระบุสถานะกลับใช้ได้ ทดสอบแล้วว่า `[*] --> รอชำระ` แสดงผลได้ และตัวระบุที่ออกมาคือ `state-รอชำระ` เอนทิตีในไดอะแกรม ER ก็เช่นเดียวกัน แปลว่าข้อจำกัดด้านอักษรมีอยู่แค่สองประเภทเท่านั้น ไม่ใช่ทั้งหมด ตัวอย่างในหน้านี้จึงใช้อักษรไทยเป็นตัวระบุตรง ๆ ได้
การไม่เว้นวรรคของภาษาไทยช่วยกันกับดักช่องว่างไว้ได้เกือบหมด
เป็นข้อได้เปรียบที่แท้จริงและควรพูดถึง ในภาษาตะวันตก ภาษาเวียดนาม และภาษาอินโดนีเซีย ช่องว่างในตัวระบุสถานะทำให้สถานะเดียวแตกเป็นกล่องละคำอย่างเงียบ ๆ ภาษาไทยเขียนติดกัน ชื่ออย่าง `รอชำระเงิน` จึงเป็นโทเคนเดียวโดยธรรมชาติและไม่แตก แต่อย่าวางใจจนเกินไป เพราะเรายังเว้นวรรคคั่นวลีและเว้นรอบตัวเลขกับอักษรละติน ชื่อสถานะอย่าง `รอชำระ 24 ชั่วโมง` จึงยังแตกอยู่ดี
ความสูงเพิ่มราว 114 พิกเซลต่อสถานะ
วัดแล้ว สามสถานะให้ viewBox ราว 91×348 ส่วนสี่สิบสถานะให้ 100×4566 คือราว 114 พิกเซลต่อสถานะ เช่นเดียวกับผังงาน ความกว้างแทบไม่ขยับ เครื่องสถานะโตลงข้างล่าง ถ้าวงจรชีวิตยาวและแตกแขนงน้อย การใส่ `direction LR` ลงในไดอะแกรมคือทางแก้ตามปกติ
ทั้ง stateDiagram และ stateDiagram-v2 แสดงผลได้ และนั่นแหละคือกับดัก
มักอ่านเจอว่า «ถ้าไม่ใช้ `stateDiagram-v2` จะไม่แสดงผลอะไรเลย» ในรุ่น 11.12.2 นั่นไม่จริง คำสำคัญทั้งสองแสดงผลได้โดยไม่มีข้อผิดพลาด สิ่งที่ต่างคือคุณภาพของการจัดวาง โดยเฉพาะกับสถานะประกอบและสถานะคู่ขนาน และเมื่อใช้ตัวเก่าก็ไม่มีคำเตือนใด ๆ ปรากฏ ถ้าสถานะประกอบดูอัดแน่นหรือลูกศรวิ่งอ้อมแปลก ๆ ให้ดูก่อนว่าคุณเปิดไดอะแกรมด้วยคำสำคัญตัวไหน ก่อนจะลงมือเขียนใหม่
ป้ายกำกับเป็น HTML การส่งออก PNG จึงวาดใหม่
เช่นเดียวกับผังงาน ไดอะแกรมคลาส และ ER ป้ายกำกับของสถานะก็ถูกวาดภายใน `<foreignObject>` ในไฟล์ SVG เนื่องจากเบราว์เซอร์ปฏิเสธที่จะแปลงสิ่งนั้นเป็นภาพแรสเตอร์บนแคนวาส การส่งออก PNG ของเว็บนี้จึงวาดไดอะแกรมใหม่ด้วยป้ายกำกับข้อความ SVG ล้วนก่อน ไฟล์ PNG ออกมาถูกต้องและเต็มขนาด ส่วนการจัดวางตัวอักษรต่างจากบนจอเพียงเล็กน้อยมาก
เมื่อใดควรใช้ไดอะแกรมประเภทอื่น
ถ้าป้ายกำกับของคุณเป็นการกระทำ เช่น ตรวจสอบ ส่ง ลองใหม่ แสดงว่าคุณกำลังอธิบายกระบวนการ ไม่ใช่วงจรชีวิต และตัวเลือกที่ซื่อตรงคือผังงาน สัญญาณที่ชัดที่สุดคือคุณตอบไม่ได้ว่า «สิ่งที่อยู่ในสถานะนี้คืออะไร»
ถ้ามีหลายองค์ประกอบที่ต่างก็มีวงจรชีวิตของตัวเอง และสิ่งที่น่าสนใจคือปฏิสัมพันธ์ระหว่างกัน การทำไดอะแกรมสถานะหนึ่งอันต่อองค์ประกอบ บวกกับไดอะแกรมลำดับหนึ่งอันสำหรับปฏิสัมพันธ์ ย่อมดีกว่าเครื่องสถานะยักษ์เพียงเครื่องเดียว
ถ้าทุกสถานะเชื่อมกับทุกสถานะ ไดอะแกรมจะพันกันยุ่งไม่ว่าคุณจะวาดอย่างไร โดยทั่วไปนั่นแปลว่าสิ่งที่คุณไล่เรียงมาเป็นสถานะนั้น แท้จริงแล้วคือธงที่ประกอบกันได้อย่างอิสระ ในกรณีนั้นตารางของชุดที่ถูกต้องบอกอะไรได้มากกว่าภาพหนึ่งภาพมาก
ไดอะแกรมประเภทอื่น
เขียนโดย Dominik Malsch · อัปเดตล่าสุด: