จัดการสถานะแอป
เก็บและอัปเดตข้อมูล UI
จัดการสถานะแอป เป็นบทเรียน Learn Rust Coding ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Learn Rust Coding และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
สถานะในโครงสร้าง App
ใน egui สถานะแอปพลิเคชันของคุณคือข้อมูล Rust ปกติที่อยู่ในโครงสร้างซึ่งนำ eframe::App ไปใช้ ไม่มีที่เก็บพิเศษหรือโมเดลที่เฟรมเวิร์กเป็นเจ้าของ
ฟิลด์ต่าง ๆ เก็บทุกอย่างไว้ ได้แก่ อินพุตของแบบฟอร์ม ตัวเลือก ข้อมูลที่โหลด และแฟล็ก ในแต่ละเฟรม update จะอ่านและแก้ไขฟิลด์เหล่านี้โดยตรงผ่าน &mut self
struct App {
query: String,
results: Vec<Item>,
selected: Option<usize>,
loading: bool,
}การสร้างค่าเริ่มต้น
eframe สร้างแอปของคุณใน closure ที่ส่งให้ run_native รูปแบบที่ง่ายที่สุดคือ #[derive(Default)] และสร้างด้วย App::default()
สำหรับค่าเริ่มต้นที่ไม่ใช่ค่าเริ่มต้นทั่วไป ให้ใช้ Default ด้วยตนเอง หรือเพิ่มตัวสร้างที่รับ CreationContext เพื่อการตั้งค่า เช่น แบบอักษรหรือการเก็บสถานะ
impl App {
fn new(cc: &eframe::CreationContext) -> Self {
let mut app = Self::default();
app.dark = cc.egui_ctx.style().visuals.dark_mode;
app
}
}แหล่งข้อมูลจริงเพียงแหล่งเดียว
เนื่องจากวิดเจ็ตยืม &mut ไปยังฟิลด์ที่เก็บค่า โครงสร้างจึงเป็นแหล่งข้อมูลจริงเพียงแหล่งเดียวเสมอ ไม่มีสถานะมุมมองแยกต่างหากที่ต้องคอยทำให้สอดคล้องกัน
ช่องทำเครื่องหมายที่ผูกกับ &mut self.enabled จะแสดงและอัปเดตค่า bool นั้นโดยตรง หากอ่านค่าจากที่อื่นในเฟรมเดียวกัน คุณจะเห็นค่าปัจจุบัน
ui.checkbox(&mut self.enabled, "Enabled");
if self.enabled {
ui.label("Feature is on");
}เลื่อนการแก้ไขค่า
ปัญหาการยืมที่พบได้ละเอียดอ่อนคือ คุณมักวนซ้ำใน Vec ขณะต้องการลบสมาชิกออก คุณไม่สามารถแก้ไขเวกเตอร์ในขณะที่ยังมีการยืมแบบอ่านร่วมอยู่ได้
วิธีแก้คือบันทึกการกระทำที่ต้องการไว้ในตัวแปรเฉพาะที่ระหว่างลูป แล้วนำไปใช้หลังจบลูป
let mut to_remove = None;
for (i, item) in self.items.iter().enumerate() {
ui.horizontal(|ui| {
ui.label(&item.name);
if ui.button("x").clicked() { to_remove = Some(i); }
});
}
if let Some(i) = to_remove { self.items.remove(i); }หน้าจอที่ขับเคลื่อนด้วย Enum
สำหรับแอปที่มีหลายหน้าจอ ให้จำลองมุมมองปัจจุบันเป็นฟิลด์ชนิด enum เมธอด update จะจับคู่ค่าดังกล่าวเพื่อเลือกว่าอะไรควรถูกวาด
วิธีนี้ทำให้การนำทางชัดเจนและครอบคลุมทุกกรณี คอมไพเลอร์จะบังคับให้คุณจัดการทุกหน้าจอ จึงกำจัดข้อผิดพลาดด้านการกำหนดเส้นทางได้ทั้งกลุ่ม
enum Screen { Home, Settings, About }
match self.screen {
Screen::Home => self.draw_home(ui),
Screen::Settings => self.draw_settings(ui),
Screen::About => self.draw_about(ui),
}หน่วยความจำ egui สำหรับสถานะ UI
สถานะบางอย่างเกี่ยวข้องกับ UI ล้วน ๆ ไม่ใช่โดเมนของคุณ เช่น ส่วนที่ยุบ ตำแหน่งการเลื่อน และสถานะการลาก egui จัดเก็บสิ่งเหล่านี้ไว้ใน ctx.memory โดยมีวิดเจ็ต Id เป็นกุญแจ
คุณแทบไม่จำเป็นต้องแตะต้องมันโดยตรง แต่ ctx.data_mut ช่วยให้คุณเก็บค่าเล็ก ๆ แยกตาม id ซึ่งคงอยู่ระหว่างเฟรมได้โดยไม่ทำให้โครงสร้างแอปมีฟิลด์รกรุงรัง
let id = egui::Id::new("my_toggle");
let mut open = ctx.data_mut(|d| d.get_temp::<bool>(id).unwrap_or(false));
ui.checkbox(&mut open, "Open");
ctx.data_mut(|d| d.insert_temp(id, open));งานเบื้องหลังด้วยแชนเนล
อย่าบล็อก update ด้วยการรับส่งข้อมูลที่ช้า เพราะจะทำให้ลูป 60fps หยุดชะงัก ให้สร้างเธรดแล้วสื่อสารผ่านแชนเนล std::sync::mpsc
ตัวทำงานจะส่งผลลัพธ์กลับมา ส่วน update จะดึงข้อมูลจากตัวรับแบบไม่บล็อกด้วย try_recv ในแต่ละเฟรม เรียก ctx.request_repaint() จากตัวทำงานเพื่อปลุก UI เมื่อข้อมูลมาถึง
if let Ok(msg) = self.rx.try_recv() {
self.results = msg;
self.loading = false;
}การสร้างตัวทำงาน
โคลน Context และตัวส่งเข้าไปในเธรด การโคลนมีค่าใช้จ่ายต่ำ เพราะภายในเป็น Arc และช่วยให้ตัวทำงานร้องขอการวาดใหม่ได้
ย้ายการเป็นเจ้าของอินพุตของงานเข้าไปใน closure เพื่อให้ผ่านตัวตรวจสอบการยืมและหลีกเลี่ยงปัญหาอายุการใช้งาน
let tx = self.tx.clone();
let ctx = ctx.clone();
std::thread::spawn(move || {
let data = expensive_load();
let _ = tx.send(data);
ctx.request_repaint();
});การเก็บสถานะถาวร
eframe สามารถทำให้แอปของคุณเป็นข้อมูลอนุกรมระหว่างการทำงานแต่ละครั้งได้ เปิดใช้ฟีเจอร์ persistence ใช้ serde::Serialize / Deserialize และนำ App::save ไปใช้
เมื่อเริ่มต้น ให้อ่านสถานะที่บันทึกไว้จาก cc.storage ในตัวสร้าง และใช้ค่าเริ่มต้นสำรองเมื่อไม่มีข้อมูลที่จัดเก็บไว้
fn save(&mut self, storage: &mut dyn eframe::Storage) {
eframe::set_value(storage, eframe::APP_KEY, self);
}
// in new():
if let Some(s) = cc.storage {
if let Some(app) = eframe::get_value(s, eframe::APP_KEY) { return app; }
}ทำให้ update มีค่าใช้จ่ายต่ำ
เนื่องจาก update ทำงานในทุกเฟรม จึงควรหลีกเลี่ยงการคำนวณข้อมูลอนุพันธ์ที่มีค่าใช้จ่ายสูงซ้ำที่นั่น ให้แคชผลลัพธ์ไว้ในฟิลด์และคำนวณใหม่เฉพาะเมื่ออินพุตเปลี่ยน
รูปแบบที่พบได้บ่อยคือ ติดตามแฟล็ก dirty ที่ตั้งค่าด้วย Response::changed() แล้วสร้างแคชใหม่ที่ต้น update เฉพาะเมื่อมีการเปลี่ยนแปลง
if self.dirty {
self.filtered = self.filter_items();
self.dirty = false;
}
for item in &self.filtered { ui.label(&item.name); }การเป็นเจ้าของช่วยให้ทุกอย่างชัดเจน
แนวคิดสำคัญที่พบซ้ำคือ egui อาศัยการเป็นเจ้าของของ Rust แทนเฟรมเวิร์กสำหรับจัดการสถานะ โครงสร้างของคุณเป็นเจ้าของข้อมูล &mut self มอบสิทธิ์เข้าถึงแบบผูกขาดในแต่ละเฟรม และเธรดสื่อสารกันผ่านแชนเนล
ไม่มีการสมัครรับข้อมูลที่ซ่อนอยู่หรือกราฟ callback ดังนั้นการทำความเข้าใจว่าสถานะเปลี่ยนเมื่อใดและอย่างไรจึงยังคงอยู่ในขอบเขตที่ชัดเจนและตรวจสอบได้
ตรวจสอบความเข้าใจ
คุณควรดำเนินการรับข้อมูลจากเครือข่ายที่ช้าอย่างไร
สรุป
สถานะแอปคือฟิลด์ปกติบนโครงสร้าง eframe::App ซึ่งถูกแก้ไขผ่าน &mut self ในแต่ละเฟรม ทำให้โครงสร้างเป็นแหล่งข้อมูลจริงเพียงแหล่งเดียว
เลื่อนการแก้ไขค่าระหว่างการวนซ้ำ จำลองหน้าจอด้วย enum แยกงานที่ช้าไปยังเธรดด้วยแชนเนล และเก็บสถานะถาวรด้วยที่เก็บข้อมูลของ eframe ทำให้ update มีค่าใช้จ่ายต่ำด้วยการแคชข้อมูลอนุพันธ์ไว้เบื้องหลังแฟล็กสกปรก ต่อไป: การจัดแพ็กเกจแอปเพื่อเผยแพร่
คำถามที่พบบ่อย
บทเรียน “จัดการสถานะแอป” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “จัดการสถานะแอป” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Learn Rust Coding ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “จัดการสถานะแอป”
เก็บและอัปเดตข้อมูล UI คุณปฏิบัติ Learn Rust Coding ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Learn Rust Coding หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Learn Rust Coding บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “จัดการสถานะแอป” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Learn Rust Coding นี้ได้ไหม
ได้ บทเรียน Learn Rust Coding ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ