localStorage vs sessionStorage
Lifetime
หัวข้อที่มีชื่อว่า “Lifetime”ความแตกต่างที่สำคัญที่สุดระหว่าง store ทั้งสองคือข้อมูลอยู่ได้นานแค่ไหน:
localStorage
- ข้อมูลอยู่ถาวรจนกว่าจะถูกลบอย่างชัดเจนด้วย
removeItemหรือclearหรือผู้ใช้ล้างข้อมูล browser - การปิดทุกแท็บ ปิด browser หรือรีสตาร์ทเครื่อง ไม่ ทำให้ข้อมูลหายไป
- จึงเหมาะกับ preferences, ธีม หรือค่าที่ cache ไว้ซึ่งควรอยู่รอดข้าม session (เช่น ปุ่มสลับ dark mode, ภาษาที่บันทึกไว้)
sessionStorage
- ข้อมูลอยู่เฉพาะตลอดอายุของแท็บ browser (หรือหน้าต่าง) ที่ถูกสร้างขึ้นเท่านั้น
- ทันทีที่แท็บนั้นถูกปิด ข้อมูลก็หายไป — กู้คืนไม่ได้
- การเปิดแท็บใหม่ไปยัง URL เดิมจะเริ่มต้นด้วย
sessionStorageที่ว่างเปล่าใหม่ - จึงเหมาะกับ state เฉพาะแท็บที่ไม่ควรรั่วข้ามแท็บ (เช่น ขั้นตอนปัจจุบันของ wizard หลายขั้น, token แบบใช้ครั้งเดียว, ฉบับร่างฟอร์มชั่วคราว)
ขอบเขตของแท็บและหน้าต่าง (Tab and window scope)
หัวข้อที่มีชื่อว่า “ขอบเขตของแท็บและหน้าต่าง (Tab and window scope)”ขอบเขตคือจุดที่ API ทั้งสองต่างกันอย่างละเอียดอ่อนที่สุด:
// Tab A:localStorage.setItem('demo:user', 'Ada');// Tab B on the same origin sees 'Ada' immediately.
sessionStorage.setItem('demo:step', '2');// Tab B has its OWN sessionStorage — it cannot see Tab A's 'step'.มีรายละเอียดปลีกย่อยอยู่อย่างหนึ่ง: เมื่อผู้ใช้เปิดลิงก์ด้วย window.open() หรือ Ctrl+click (ซึ่งทำสำเนาแท็บ) แท็บใหม่จะได้รับ สำเนา ของ sessionStorage จากแท็บแม่ ณ ขณะที่ถูกเปิดขึ้น หลังจากนั้น store ทั้งสองจะเป็นอิสระต่อกัน — การเปลี่ยนแปลงในตัวหนึ่งจะไม่ส่งผลต่ออีกตัว
ขอบเขตของ origin (Origin scope)
หัวข้อที่มีชื่อว่า “ขอบเขตของ origin (Origin scope)”store ทั้งสองมีขอบเขตตาม origin: scheme + hostname + port
| URL | Origin |
|---|---|
https://example.com | https://example.com:443 |
https://app.example.com | https://app.example.com:443 (ต่างกัน!) |
http://example.com | http://example.com:80 (ต่าง scheme!) |
https://example.com:8080 | https://example.com:8080 (ต่าง port!) |
ดังนั้น https://app.example.com และ https://api.example.com ต่างก็มี localStorage ของตัวเองที่แยกขาดจากกันโดยสิ้นเชิง แม้ว่าจะใช้ registrable domain เดียวกันก็ตาม
รันได้: เขียนลง store ทั้งสอง
หัวข้อที่มีชื่อว่า “รันได้: เขียนลง store ทั้งสอง”รันโค้ดด้านล่างเพื่อดูทั้งสอง store ถูกเขียนและอ่านกลับมาภายใน context ของหน้าเดียวกัน
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
localStorage | ข้อมูลอยู่ถาวรและแชร์ข้ามทุกแท็บของ origin เดียวกัน เหมาะกับ preference ระยะยาว | เขียนจากแท็บไหนก็กระทบทุกแท็บ เสี่ยงต่อ race condition หากหลายแท็บเขียนพร้อมกัน |
sessionStorage | ขอบเขตแคบแค่แท็บเดียว ลดความเสี่ยงของข้อมูลรั่วข้ามแท็บ และหายไปเองเมื่อปิดแท็บ | ต้องตั้งค่าใหม่ทุกครั้งที่เปิดแท็บใหม่ ใช้เก็บ preference ระยะยาวไม่ได้ |
แชร์ state ผ่าน sessionStorage ตอนทำสำเนาแท็บ | แท็บใหม่ได้ context เดิมทันทีตอนเปิดขึ้นมา (เช่น ทำสำเนากลางฟอร์ม) | เป็นสำเนาแบบครั้งเดียว หลังจากนั้น sync กันเองไม่ได้อีก อาจทำให้ข้อมูลสองแท็บ diverge |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- คิดว่า
sessionStoragesync กันข้ามแท็บเหมือนlocalStorage— แต่ละแท็บมีsessionStorageแยกเป็นของตัวเอง เขียนใน Tab A จะไม่มีทางเห็นใน Tab B - เก็บ auth token ใน
localStorage— เพราะ JavaScript อ่านได้โดยตรง หาก origin โดน XSS token จะถูกขโมยได้ง่าย ต่างจาก HttpOnly cookie ที่ JS แตะไม่ถึง - สับสนระหว่าง subdomain กับ origin เดียวกัน —
app.example.comและapi.example.comมีlocalStorageแยกกันคนละก้อนเสมอ แม้ registrable domain จะเหมือนกัน
💡 ตัวอย่างจากของจริง
Theme/dark-mode toggle — เว็บส่วนใหญ่เก็บค่า preference นี้ด้วย
localStorageเพราะต้องการให้ค่าคงอยู่ข้าม session และแชร์ทุกแท็บที่เปิดพร้อมกันMulti-step checkout wizard — เว็บอีคอมเมิร์ซใช้
sessionStorageเก็บ step ปัจจุบันและข้อมูลฟอร์มระหว่างทาง เพื่อไม่ให้ข้อมูลนี้รั่วไปยังแท็บอื่นหรือค้างอยู่หลังผู้ใช้ปิดแท็บไปแล้ว