Các phương thức lưu trữ dữ liệu trên trình duyệt (Client-side Storage)

- Cookie – Giải pháp cổ điển nhưng không lỗi thời.
- LocalStorage – Lưu trữ bền vững, dung lượng lớn
- SessionStorage – Lưu trữ ngắn hạn theo phiên làm việc
- IndexedDB – Cơ sở dữ liệu NoSQL ngay trên trình duyệt
- Cache API – Giải pháp lưu trữ tài nguyên cho ứng dụng Offline
- Bảng so sánh nhanh các phương thức lưu trữ
- Mở rộng
- Lời kết
Khi xây dựng một ứng dụng web, việc lưu trữ dữ liệu ở phía client (trình duyệt của người dùng) đóng vai trò cực kỳ quan trọng. Nó giúp tối ưu hóa tốc độ tải trang, nâng cao trải nghiệm người dùng (UX) bằng cách ghi nhớ trạng thái đăng nhập, giỏ hàng, cấu hình giao diện… mà không cần liên tục gửi truy vấn lên server.
Trong bài viết này, chúng ta sẽ cùng tìm hiểu và so sánh các phương pháp lưu trữ dữ liệu phổ biến nhất trên trình duyệt hiện nay, cũng như cách sử dụng JavaScript để tương tác và điều khiển chúng một cách hiệu quả trong dự án thực tế.
Cookie – Giải pháp cổ điển nhưng không lỗi thời.
Cookie là những tệp tin văn bản nhỏ được máy chủ gửi về và lưu trữ trực tiếp trên trình duyệt của người dùng.
- Cơ chế: Lưu tệp dữ liệu nhỏ trên trình duyệt, tự động đính kèm vào header của mọi request HTTP gửi lên server.
- Dung lượng: Tối đa khoảng 4 KB.
- Tốc độ truy xuất: Chậm nhất (do liên tục truyền tải qua mạng, gây network overhead).
- Thời gian sống: Thiết lập tùy ý qua thuộc tính
ExpireshoặcMax-Age. - Trường hợp sử dụng: Quản lý phiên đăng nhập (Session ID), lưu cấu hình ngôn ngữ, tracking cơ bản.
- Điểm cộng: Tự động đồng bộ với Server mà không cần code JavaScript can thiệp.
- Hạn chế: Dung lượng siêu nhỏ và trực tiếp gây ảnh hưởng đến hiệu năng mạng khi tải trang.
// 1. Tạo Cookie lưu tên người dùng trong 7 ngày const maxAge = 7 * 24 * 60 * 60; // 7 ngày tính bằng giây document.cookie = "username=TienDev; max-age=" + maxAge + "; path=/; Secure"; // 2. Đọc tất cả Cookie trên trình duyệt console.log(document.cookie); // Output dạng chuỗi: "username=TienDev; other_key=value"
LocalStorage – Lưu trữ bền vững, dung lượng lớn
Là một bộ nhớ lưu trữ cục bộ của trình duyệt thuộc Web Storage API, giúp lưu trữ các dữ liệu không nhạy cảm ở phía client.
- Cơ chế: Lưu trữ dữ liệu dạng Key-Value (chỉ nhận String) trực tiếp trong bộ nhớ trình duyệt.
- Dung lượng: Khoảng 5 MB – 10 MB.
- Tốc độ truy xuất: Rất nhanh (Đồng bộ).
- Thời gian sống: Vĩnh viễn (chỉ mất khi bị xóa bằng code hoặc dọn lịch sử duyệt web).
- Trường hợp sử dụng: Lưu chế độ giao diện (Dark/Light mode), lưu nháp nội dung form, lưu sản phẩm yêu thích, giỏ hàng.
- Điểm cộng: Cực kỳ dễ sử dụng với cú pháp đọc/ghi siêu ngắn gọn.
- Hạn chế: Dễ bị tấn công XSS đánh cắp dữ liệu và dễ gây đơ giao diện (blocking) nếu ghi dữ liệu lớn liên tục.
// 1. Lưu cài đặt giao diện tối (Dark mode) localStorage.setItem("theme", "dark"); // 2. Đọc dữ liệu ra sử dụng const currentTheme = localStorage.getItem("theme"); console.log(currentTheme); // Output: "dark" // 3. Xóa một key cụ thể hoặc xóa sạch LocalStorage localStorage.removeItem("theme"); // localStorage.clear(); // Xóa toàn bộ dữ liệu trong LocalStorage
SessionStorage – Lưu trữ ngắn hạn theo phiên làm việc
Là một bộ lưu trữ thuộc Web Storage API tương tự LocalStorage nhưng phạm vi hoạt động được giới hạn nghiêm ngặt theo từng phiên làm việc của tab.
- Cơ chế: Tương tự LocalStorage nhưng giới hạn phạm vi trong một phiên làm việc của tab.
- Dung lượng: Khoảng 5 MB.
- Tốc độ truy xuất: Rất nhanh (Đồng bộ).
- Thời gian sống: Bị xóa ngay lập tức khi người dùng đóng tab trình duyệt.
- Trường hợp sử dụng: Lưu dữ liệu nhạy cảm ngắn hạn, trạng thái các bước trong form đăng ký (Multi-step form).
- Điểm cộng: Tự động dọn dẹp theo Tab, tránh tích tụ rác dữ liệu sau khi sử dụng.
- Hạn chế: Phạm vi lưu trữ cực kỳ hẹp, mở một tab mới cùng URL cũng không thể nhận chung dữ liệu.
// 1. Lưu dữ liệu bước hiện tại của Form đăng ký sessionStorage.setItem("step_completed", "2"); // 2. Lấy dữ liệu ra kiểm tra const currentStep = sessionStorage.getItem("step_completed"); console.log(currentStep); // Output: "2"
IndexedDB – Cơ sở dữ liệu NoSQL ngay trên trình duyệt
Là một hệ thống cơ sở dữ liệu phi quan hệ (NoSQL) chuẩn hóa được tích hợp sẵn bên trong trình duyệt để xử lý dữ liệu quy mô lớn.
- Cơ chế: Cơ sở dữ liệu phi quan hệ (NoSQL) lưu Object, file, blob, hoạt động thông qua các Transaction.
- Dung lượng: Rất lớn (không giới hạn cứng, phụ thuộc vào ổ cứng trống của thiết bị).
- Tốc độ truy xuất: Nhanh (Bất đồng bộ), không chặn luồng xử lý giao diện (UI Thread/Main Thread).
- Thời gian sống: Vĩnh viễn (chỉ mất khi xóa bằng code hoặc trình duyệt tự dọn dẹp do đầy ổ cứng).
- Trường hợp sử dụng: Ứng dụng chạy offline (PWA), lưu trữ dữ liệu game nặng, cache dữ liệu lớn từ API.
- Điểm cộng: Hỗ trợ truy vấn phức tạp (Index) và lưu được dữ liệu lớn, các file vật lý (Blob/File).
- Hạn chế: Cú pháp rất phức tạp, dài dòng và khó tiếp cận cho người mới bắt đầu.
Trong IndexedDB, mọi thao tác đọc/ghi dữ liệu bắt buộc phải nằm trong một Transaction (Giao dịch). Điều này giúp đảm bảo tính toàn vẹn của dữ liệu (nếu một bước trong chuỗi thao tác bị lỗi, toàn bộ giao dịch sẽ bị hủy bỏ/rollback, tránh việc dữ liệu bị nửa vời).
// Bước 1: Khởi tạo/Mở database có tên "AppDB" phiên bản 1
const request = indexedDB.open("AppDB", 1);
// Sự kiện này chỉ chạy một lần duy nhất khi khởi tạo hoặc nâng cấp version
request.onupgradeneeded = (event) => {
const db = event.target.result;
// Tạo một bảng (Object Store) tên "users" với khóa chính tự tăng "id"
db.createObjectStore("users", { keyPath: "id", autoIncrement: true });
};
// Khi mở kết nối thành công, chúng ta mới thực hiện Transaction
request.onsuccess = (event) => {
const db = event.target.result;
console.log("Mở IndexedDB thành công!");
// ==========================================
// BƯỚC 2: THỰC HIỆN TRANSACTION THÊM DỮ LIỆU
// ==========================================
// 1. Tạo một giao dịch ghi (readwrite) vào bảng "users"
const transaction = db.transaction("users", "readwrite");
// 2. Trỏ tới bảng "users"
const objectStore = transaction.objectStore("users");
// 3. Thực hiện hành động thêm dữ liệu
const newUser = { name: "TienDev", role: "Developer" };
const addRequest = objectStore.add(newUser);
addRequest.onsuccess = () => {
console.log("Thêm user mới thành công!");
};
addRequest.onerror = (err) => {
console.error("Thêm user thất bại: ", err);
};
// Giao dịch hoàn tất an toàn và dữ liệu được ghi xuống đĩa
transaction.oncomplete = () => {
console.log("Giao dịch (Transaction) hoàn tất!");
};
};
Cache API – Giải pháp lưu trữ tài nguyên cho ứng dụng Offline
Là hệ thống lưu trữ bộ nhớ đệm của trình duyệt được thiết kế riêng để lưu trữ và truy xuất các yêu cầu cũng như phản hồi mạng trực tiếp.
- Cơ chế: Lưu trữ các cặp đối tượng
RequestvàResponseđược quản lý trực tiếp bởi trình duyệt. - Dung lượng: Rất lớn (chia sẻ chung hạn mức bộ nhớ đệm với IndexedDB).
- Tốc độ truy xuất: Rất nhanh (Bất đồng bộ), không chặn luồng xử lý giao diện.
- Thời gian sống: Vĩnh viễn (chỉ mất khi xóa bằng code hoặc trình duyệt tự dọn dẹp do đầy ổ cứng).
- Trường hợp sử dụng: Lưu cache các tài nguyên tĩnh (HTML, CSS, JS, hình ảnh) kết hợp với Service Worker.
- Điểm cộng: Trợ thủ đắc lực giúp PWA hoạt động Offline mượt mà, tăng tốc tải trang tối đa.
- Hạn chế: Chỉ tối ưu cho tài nguyên tĩnh, cấu hình cập nhật (invalidation) phức tạp dễ khiến người dùng bị kẹt ở bản cache cũ.
// 1. Mở một kho cache có tên 'v1-assets'
caches.open('v1-assets').then((cache) => {
// 2. Lưu trữ các tài nguyên tĩnh vào bộ nhớ cache
cache.addAll([
'/index.html',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
]).then(() => {
console.log('Đã cache tài nguyên tĩnh thành công!');
});
});
Bảng so sánh nhanh các phương thức lưu trữ
| Tiêu chí | Cookie | LocalStorage | SessionStorage | IndexedDB | Cache API |
|---|---|---|---|---|---|
| Dung lượng | ~ 4 KB | ~ 5 – 10 MB | ~ 5 MB | Không giới hạn cứng (tùy ổ cứng) | Rất lớn (chia sẻ với IndexedDB) |
| Thời gian sống | Thiết lập qua Expires | Vĩnh viễn | Khi đóng Tab | Vĩnh viễn | Vĩnh viễn (hoặc do trình duyệt tự dọn) |
| Tự động gửi lên Server | Có (mỗi request) | Không | Không | Không | Không |
| Kiểu dữ liệu hỗ trợ | Chỉ chuỗi (String) | Chỉ chuỗi (String) | Chỉ chuỗi (String) | Object phức tạp, Blobs, Files… | Cặp Request / Response |
| Cơ chế xử lý | Đồng bộ | Đồng bộ | Đồng bộ | Bất đồng bộ | Bất đồng bộ |
| Tốc độ truy xuất | Chậm nhất (ảnh hưởng bởi mạng) | Rất nhanh | Rất nhanh | Nhanh (tối ưu cho dữ liệu lớn) | Rất nhanh |
⚠️ Lưu ý đặc biệt về Bảo mật (Security Best Practices)
Một sai lầm rất phổ biến của các lập trình viên mới là lưu trữ các thông tin nhạy cảm (như mật khẩu, thông tin thẻ tín dụng, JWT Token chứa thông tin quyền hạn cao…) vào LocalStorage.
Lý do là bất kỳ đoạn mã JavaScript độc hại nào chạy trên trang web của bạn (tấn công XSS – Cross-Site Scripting) đều có thể dễ dàng đọc sạch dữ liệu trong LocalStorage. Vì vậy, đối với các dữ liệu nhạy cảm cần bảo mật tuyệt đối, hãy cân nhắc sử dụng HttpOnly Cookie hoặc quản lý tập trung ở phía server thông qua các cơ sở dữ liệu chuyên dụng.
Mở rộng
Thực chất, nếu xét tất cả các ngóc ngách của trình duyệt và các tiêu chuẩn web (cả cũ lẫn mới), chúng ta vẫn còn các phương thức lưu trữ đặc thù sau đây. Các phương thức này thường ít khi được dùng đơn lẻ để lưu trữ dữ liệu thông thường, nhưng mình đưa vào bài viết dưới dạng một mục “Mở rộng” để đào sâu và tổng hợp đầy đủ hơn:
Memory Storage (Bộ nhớ RAM tạm thời của Tab)
Là việc lưu trữ trạng thái dữ liệu trực tiếp trên bộ nhớ RAM vận hành của trình duyệt thông qua các cấu trúc khai báo biến JavaScript.
- Cơ chế: Lưu trực tiếp trong các biến, mảng, đối tượng hoặc state của JavaScript (React/Vue state,
window). - Dung lượng: Phụ thuộc hoàn toàn vào lượng RAM trống của thiết bị.
- Tốc độ truy xuất: Nhanh nhất vô địch (thời gian truy cập gần như bằng 0).
- Thời gian sống: Tạm thời nhất (biến mất hoàn toàn ngay khi nhấn F5 hoặc đóng tab).
- Trường hợp sử dụng: Lưu trạng thái hiển thị giao diện (đóng/mở modal, menu), lưu dữ liệu API tạm thời để render.
- Điểm cộng: An toàn trước tấn công XSS do không lưu trữ vật lý xuống bộ nhớ trình duyệt.
- Hạn chế: Không bền vững, trang tự reload là toàn bộ dữ liệu đang xử lý lập tức biến mất.
URL Parameters & Query Strings (Lưu trữ trạng thái qua thanh địa chỉ)
Là phương thức lưu trữ và truyền trạng thái của trang web thông qua việc đính kèm các tham số trực tiếp vào chuỗi định vị tài nguyên URL.
- Cơ chế: Đính kèm trực tiếp trạng thái lên đường dẫn URL dưới dạng cặp key-value sau dấu ? (Ví dụ: https://tiendevblog.wordpress.com/blog/?query-9-page=2).
- Dung lượng: Cực kỳ nhỏ (giới hạn bởi độ dài URL của trình duyệt, thường khoảng 2000 ký tự).
- Tốc độ truy xuất: Rất nhanh (Đồng bộ), đọc trực tiếp qua
window.location. - Thời gian sống: Vĩnh viễn (đi kèm theo đường link).
- Trường hợp sử dụng: Lưu trạng thái bộ lọc tìm kiếm (filter), số trang đang đứng (pagination), mã giới thiệu.
- Điểm cộng: Rất dễ chia sẻ, người nhận link sẽ truy cập đúng chính xác trạng thái được lưu.
- Hạn chế: Lộ thông tin hoàn toàn trên thanh địa chỉ, tuyệt đối không dùng cho dữ liệu riêng tư hay nhạy cảm.
File System Access API (Đọc/Ghi trực tiếp vào ổ cứng người dùng)
Là một API trình duyệt hiện đại cho phép ứng dụng web có thể tương tác, chỉnh sửa trực tiếp với các tệp tin hệ thống nằm trên thiết bị vật lý của người dùng.
- Cơ chế: Giao tiếp trực tiếp với hệ thống tập tin của thiết bị sau khi được người dùng cấp quyền.
- Dung lượng: Không giới hạn (phụ thuộc hoàn toàn vào dung lượng trống của ổ cứng).
- Tốc độ truy xuất: Bất đồng bộ (Nhanh), tốc độ thực tế phụ thuộc vào phần cứng SSD/HDD của người dùng.
- Thời gian sống: Vĩnh viễn (tồn tại như tệp tin thông thường trên máy tính, trình duyệt không thể tự xóa).
- Trường hợp sử dụng: Ứng dụng sửa ảnh/video online (Canva, Figma), trình soạn thảo code (VS Code Web).
- Điểm cộng: Cho trải nghiệm mượt mà giống hệt ứng dụng cài trên máy tính (Native App), dữ liệu do người dùng sở hữu.
- Hạn chế: Rào cản bảo mật lớn (phải hỏi quyền liên tục) và độ tương thích kém (chỉ chạy tốt trên hệ Chromium như Chrome/Edge).
OPFS (Origin Private File System)
Là một phân vùng hệ thống tập tin riêng tư, cô lập theo từng nguồn (Origin) nằm trong File System Access API.
- Cơ chế: Tạo một không gian lưu trữ ẩn đối với hệ điều hành của người dùng. Mọi thao tác đọc/ghi đều do trình duyệt quản lý ngầm trong một vùng an toàn.
- Dung lượng: Rất lớn (chia sẻ chung hạn mức bộ nhớ đệm với IndexedDB).
- Tốc độ truy xuất: Siêu nhanh (Bất đồng bộ) nhờ hỗ trợ cơ chế truy cập độc quyền (Access Handles) trong Web Workers.
- Thời gian sống: Vĩnh viễn (chỉ mất khi code chủ động xóa hoặc người dùng xóa toàn bộ dữ liệu trang web).
- Trường hợp sử dụng: Chạy các tệp cơ sở dữ liệu như SQLite (bản Wasm) trực tiếp dưới trình duyệt, lưu cache video/audio dung lượng lớn khi edit.
- Điểm cộng: Tốc độ tối hạn, đọc/ghi luồng ngầm mượt mà và hỗ trợ ghi đè trực tiếp một phần dữ liệu (In-place write).
- Hạn chế: Dữ liệu hoàn toàn bị ẩn, người dùng không thể tự quản lý, sao chép hay di chuyển file ra ngoài bằng File Explorer/Finder.
Lời kết
Qua bài viết, bạn đã hiểu rõ ưu, nhược điểm cũng như tốc độ truy xuất của từng phương pháp lưu trữ ở phía client, việc này sẽ giúp bạn đưa ra những quyết định thiết kế hệ thống tối ưu nhất. Hãy nhớ nguyên tắc cốt lõi: xác định công cụ cho đúng mục đích và luôn ưu tiên bảo mật dữ liệu của người dùng.
Ở bài viết tiếp theo, mình sẽ cùng các bạn phân tích chi tiết Các hệ thống lưu trữ dữ liệu ở phía máy chủ (Server-side Database) như PostgreSQL, MongoDB và Redis để có cái nhìn toàn diện nhất. Hãy đón đọc nhé!


