รูปแบบ UI แบบเรียลไทม์
ใช้การอัปเดตเชิงคาดการณ์เพื่อให้ผู้ใช้ได้รับการตอบสนองทันที จัดคิวในเครื่องเมื่อออฟไลน์ และเปรียบเทียบข้อมูลที่เข้ามาเพื่ออัปเดตเฉพาะรายการที่เปลี่ยนแปลงใน UI
รูปแบบ UI แบบเรียลไทม์ เป็นบทเรียน Frontend Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Frontend Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Frontend Academy มีบทเรียนทั้งหมด 4 บทเรียน
เรียลไทม์ที่ให้ประสบการณ์เหมือนเรียลไทม์
เรียลไทม์ไม่ได้หมายถึงความเร็วเพียงอย่างเดียว แต่ยังต้องตอบสนองได้ คาดการณ์ได้ และรองรับความหน่วงได้อย่างเหมาะสม รูปแบบหลักสามอย่างคือ การอัปเดตเชิงคาดหวัง การจัดคิวในเครื่อง และการเปรียบเทียบความแตกต่างอย่างชาญฉลาด
การอัปเดตเชิงคาดหวัง
อัปเดตส่วนติดต่อผู้ใช้ทันทีเมื่อผู้ใช้ดำเนินการ โดยไม่รอการยืนยันจากเซิร์ฟเวอร์ หากเซิร์ฟเวอร์ปฏิเสธ ให้ย้อนกลับการเปลี่ยนแปลง วิธีนี้ทำให้แอปตอบสนองเหมือนทันที
async function likePost(id) {
// 1. Optimistically update UI:
setLikes(l => ({ ...l, [id]: (l[id] || 0) + 1 }));
setLiked(prev => ({ ...prev, [id]: true }));
try {
await fetch(`/api/posts/${id}/like`, { method: 'POST' });
} catch (err) {
// 2. Rollback on failure:
setLikes(l => ({ ...l, [id]: (l[id] || 1) - 1 }));
setLiked(prev => ({ ...prev, [id]: false }));
toast.error('Could not like — please retry');
}
}การอัปเดตเชิงคาดหวังด้วย ID ชั่วคราว
สำหรับรายการที่สร้างขึ้น ให้กำหนด ID ชั่วคราวในเครื่องก่อน เมื่อได้รับการตอบกลับ ให้แทนที่ด้วย ID จริงจากเซิร์ฟเวอร์
async function addComment(text) {
const tempId = `tmp-${Date.now()}`;
setComments(c => [...c, { id: tempId, text, pending: true }]);
try {
const saved = await fetch('/api/comments', {
method: 'POST',
body: JSON.stringify({ text })
}).then(r => r.json());
setComments(c => c.map(m => m.id === tempId ? saved : m));
} catch (err) {
setComments(c => c.map(m => m.id === tempId ? { ...m, error: true } : m));
}
}การจัดคิวในเครื่องเมื่อออฟไลน์
เมื่อเครือข่ายขัดข้อง ให้จัดคิวการดำเนินการไว้ในเครื่องและนำกลับมาทำซ้ำเมื่อเครือข่ายกลับมา ผู้ใช้จึงทำงานต่อได้
const queue = JSON.parse(localStorage.getItem('queue') || '[]');
function enqueue(action) {
queue.push(action);
localStorage.setItem('queue', JSON.stringify(queue));
trySend();
}
async function trySend() {
while (queue.length && navigator.onLine) {
const action = queue[0];
try {
await fetch(action.url, action);
queue.shift();
localStorage.setItem('queue', JSON.stringify(queue));
} catch {
break; // stop until online
}
}
}
window.addEventListener('online', trySend);การเปรียบเทียบข้อมูลเรียลไทม์ที่เข้ามา
หากเซิร์ฟเวอร์ส่งรายการทั้งหมดมา ให้เปรียบเทียบกับสถานะปัจจุบันและอัปเดตเฉพาะส่วนที่เปลี่ยนแปลง วิธีนี้ช่วยหลีกเลี่ยงการวาดใหม่ที่ไม่จำเป็นและอาการแอนิเมชันกระตุก
function applyServerSnapshot(newList, currentList) {
const current = new Map(currentList.map(i => [i.id, i]));
const result = newList.map(item => {
const prev = current.get(item.id);
if (!prev) return { ...item, _state: 'new' };
if (prev.updatedAt !== item.updatedAt) return { ...item, _state: 'updated' };
return prev;
});
return result;
}การอัปเดตโดยใช้แพตช์
ดีกว่าการส่งภาพรวมทั้งหมด เพราะเซิร์ฟเวอร์ส่งเฉพาะฟิลด์ที่เปลี่ยนแปลงมา ให้ปรับใช้ทีละส่วน ซึ่งใช้แบนด์วิดท์น้อยกว่าและแสดงผลได้เร็วกว่า
// Server sends: { type: 'patch', id: 42, fields: { likes: 11 } }
ws.on('message', (msg) => {
if (msg.type === 'patch') {
setPosts(posts => posts.map(p =>
p.id === msg.id ? { ...p, ...msg.fields } : p
));
}
});การจำกัดความถี่ของการอัปเดตจำนวนมาก
สตรีมบางชนิดส่งการอัปเดตหลายครั้งต่อวินาที เช่น ตำแหน่งเมาส์และราคาหุ้น ให้จำกัดการอัปเดตส่วนติดต่อผู้ใช้ไว้ที่ประมาณ 60 Hz เพื่อไม่ให้เบราว์เซอร์กระตุก
import { throttle } from 'lodash-es';
const applyUpdate = throttle((data) => {
setData(data);
}, 16); // ~60fps
ws.on('tick', applyUpdate);ตัวบ่งชี้การมีอยู่
แสดงว่าใครกำลังออนไลน์หรือกำลังพิมพ์แบบเรียลไทม์ เซิร์ฟเวอร์เผยแพร่เหตุการณ์สถานะการมีอยู่ ส่วนไคลเอ็นต์แสดงรายการพร้อมรูปประจำตัว
ws.on('presence', ({ users }) => {
setOnlineUsers(users);
});
ws.on('typing', ({ user, isTyping }) => {
setTypingUsers(t => isTyping
? [...t.filter(u => u.id !== user.id), user]
: t.filter(u => u.id !== user.id));
});แบนเนอร์สถานะการเชื่อมต่อ
แสดงแบนเนอร์ขนาดเล็กเมื่อออฟไลน์หรือกำลังเชื่อมต่อใหม่ ซ่อนแบนเนอร์เมื่อเชื่อมต่อกลับมาอย่างสมบูรณ์ ผู้ใช้จะยอมรับความหน่วงได้ดีขึ้นมากเมื่อมองเห็นว่าเกิดอะไรขึ้น
{!isConnected && (
<div className="connection-banner">
Reconnecting... Your changes will sync when you're back online.
</div>
)}การแก้ไขข้อขัดแย้ง
แอปแบบเรียลไทม์ต้องรองรับการแก้ไขพร้อมกันที่ขัดแย้งกัน กลยุทธ์ที่ใช้ได้มีดังนี้: การให้การเขียนครั้งล่าสุดชนะ ซึ่งเรียบง่ายแต่ทำให้ข้อมูลบางส่วนสูญหาย เวกเตอร์รุ่น OT (การแปลงการดำเนินการ) และ CRDTs (Yjs, Automerge)
CRDTs สำหรับการทำงานร่วมกัน
CRDTs (ชนิดข้อมูลจำลองแบบไร้ข้อขัดแย้ง) ช่วยให้แก้ไขข้อมูลร่วมกันแบบเรียลไทม์ได้โดยไม่ต้องมีเซิร์ฟเวอร์ส่วนกลาง ไลบรารีที่ใช้ได้ ได้แก่ Yjs และ Automerge พบการใช้งานในแอปสไตล์ Figma, Linear และ Notion
ประสิทธิภาพ: รายการแบบจำลองเสมือน
ฟีดแบบเรียลไทม์อาจยาวขึ้นเรื่อย ๆ ให้ใช้รายการแบบจำลองเสมือน เช่น react-virtuoso และ @tanstack/react-virtual เพื่อแสดงเฉพาะรายการที่มองเห็นได้ การใช้หน่วยความจำและ CPU จึงคงที่ไม่ว่าจำนวนรายการทั้งหมดจะเท่าใด
ตรวจสอบความเข้าใจ
การอัปเดตเชิงคาดหวังในส่วนติดต่อผู้ใช้แบบเรียลไทม์คืออะไร
สรุป: รูปแบบส่วนติดต่อผู้ใช้แบบเรียลไทม์
การอัปเดตเชิงคาดหวังทำให้แอปตอบสนองเหมือนทันที และให้ย้อนกลับเมื่อเกิดข้อผิดพลาด ใช้ ID ชั่วคราวสำหรับรายการที่สร้างขึ้น จัดคิวในเครื่องเพื่อให้ทำงานต่อได้เมื่อออฟไลน์ เปรียบเทียบหรือใช้แพตช์กับการอัปเดตจากเซิร์ฟเวอร์เพื่อหลีกเลี่ยงการวาดใหม่ จำกัดความถี่ของสตรีมที่มีการอัปเดตสูง แสดงสถานะการมีอยู่ เช่น ออนไลน์และกำลังพิมพ์ รวมถึงแบนเนอร์สถานะการเชื่อมต่อ ใช้ CRDTs (Yjs, Automerge) สำหรับการแก้ไขร่วมกัน และใช้รายการแบบจำลองเสมือนกับรายการเรียลไทม์ที่ยาว
เรียนรู้ HTML ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 41
- บทเรียน
- 163
คำถามที่พบบ่อย
บทเรียน “รูปแบบ UI แบบเรียลไทม์” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “รูปแบบ UI แบบเรียลไทม์” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Frontend Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Frontend Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “รูปแบบ UI แบบเรียลไทม์”
ใช้การอัปเดตเชิงคาดการณ์เพื่อให้ผู้ใช้ได้รับการตอบสนองทันที จัดคิวในเครื่องเมื่อออฟไลน์ และเปรียบเทียบข้อมูลที่เข้ามาเพื่ออัปเดตเฉพาะรายการที่เปลี่ยนแปลงใน UI คุณปฏิบัติ Frontend Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Frontend Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Frontend Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “รูปแบบ UI แบบเรียลไทม์” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Frontend Academy นี้ได้ไหม
ได้ บทเรียน Frontend Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- WebSocket API: เปิด รับข้อความ ปิด และข้อผิดพลาด
- การเชื่อมต่อ Socket.io ฝั่งไคลเอนต์
- เหตุการณ์จากเซิร์ฟเวอร์สำหรับการสตรีมทางเดียว
- รูปแบบ UI แบบเรียลไทม์