Common browser storage schemes

preface

During development, you often encounter situations where you need to store information locally, such as tokens for permission validation, user information, buried point counts, custom skin information, or language types. In this case, browser localStorage will be used, including cookies, localStorage, sessionStorage, indexDB and other common solutions.

cookie

An HTTP Cookie (also known as a Web Cookie or browser Cookie) is a small piece of data that a server sends to a user’s browser and keeps locally. It is carried and sent to the server the next time the browser makes a request to the same server. Typically, it is used to tell the server whether two requests are from the same browser, such as to keep the user logged in.

Cookies are mainly used for the following three aspects:

  • Session state management (such as user login status, shopping cart, game score, or other information that needs to be logged)
  • Personalization (such as user-defined Settings, themes, etc.)
  • Browser behavior tracking (e.g. tracking and analyzing user behavior, etc.)

Use of cookies:

// front-end document.cookie = "name=trick"; // node response.setHeader('Set-Cookie', ['name=trick']);Copy the code

localStorage

A persistent storage mode, meaning that data will never expire unless it is manually cleaned up. It uses key-value pair to store data and saves the data to the corresponding database file according to the domain name. It can hold much more data than a Cookie.

LocalStorage:

localStorage.setItem('name', 'trick'); localStorage.getItem('name'); Localstorage.removeitem ('name'); // trick // remove localstorage.removeitem ('name'); Localstorage.clear ();Copy the code

sessionStorage

SessionStorage is similar to localStorage except that the data stored in localStorage does not have an expiration date, while the data stored in sessionStorage is cleared when the page session ends.

SessionStorage:

// Save data to sessionStorage sessionstorage. setItem('key', 'value'); Let data = sessionstorage.getitem ('key'); let data = sessionStorage.getitem ('key'); / / delete saved data from sessionStorage sessionStorage. RemoveItem (" key "); Sessionstorage.clear (); // Delete all saved data from sessionStorage.clear();Copy the code

indexDB

IndexedDB is an underlying API used by clients to store large amounts of structured data, including files and binary large objects. It is a javascript-based object-oriented database that allows the storage and retrieval of key-indexed objects, and can store any object supported by structured cloning algorithms.

Open create database

To obtain access to the database, call the open() method on the Window object’s indexedDB property, which returns an IDBRequest object. Asynchronous operations communicate with the calling program by firing events on IDBRequest objects.

let db; /** * @param dbName database name * @param dbVersion database version */ const DBOpenRequest = window.indexeddb.open ("toDoList", 4); Dbopenrequest. onerror = function(event) {console.log(' indexDB failed to start ')}; dbOpenRequest. onerror = function(event) {console.log(' indexDB failed to start ')}; DBOpenRequest.onsuccess = function(event) { db = DBOpenRequest.result; Console. log(' Started indexDB successfully ')}; DBOpenRequest. Onupgradeneeded = function (event) {/ library used to create the object table * * * * @ param tableName data table name * @ param {keyPath} is used to search the primary key of the * / const objectStore = db.createObjectStore("toDoList", { keyPath: "id" }); /** * @param indexName table column name * @param keyPath key name * @param {unique} Whether the key can have duplicate values */ objectStore.createIndex("hours", "hours", { unique: false }); objectStore.createIndex("minutes", "minutes", { unique: false }); objectStore.createIndex("day", "day", { unique: false }); };Copy the code

Database transaction

IndexedDB transactions are used to process data properties and provide static, asynchronous transactions on the database, where all data is read and written, and the transaction can be manually started and the transaction mode can be set.

/ * * * @ param dbName table name * @ param transaction mode readonly readwrite/versionchange * / const transaction = db.transaction(["toDoList"], "readwrite"); /** * @param dbName */ const objectStore = transaction.objectstore ("toDoList"); /** * const data = [{hours: 19, minutes: 30, day: 24}]; objectStore.add(data); objectStore.get(id); objectStore.put(newData); objectStore.delete(id);Copy the code

Browser storage schemes differ

features cookie localStorage sessionStorage indexDB
Data life cycle Generally, it is generated by the server. You can set the expiration time It persists unless manually cleared This is cleared when the current page or browser is closed It persists unless manually cleared
Data store size 4k 5M 5M infinite
Communicates with the server It’s carried in the header every time Don’t participate in Don’t participate in Don’t participate in

Indexdb-based periodic clearing

Based on the interview questions I have seen and actual project requirements, in addition to cookie, there is no corresponding API for setting the expiration time for the other three storage methods. However, due to the small capacity of cookie storage, single data format setting, and data retrieval difficulties, indexDB-based encapsulation is presented here. The same method can be used to encapsulate localStorage and sessionStorage.

Star type CacheDoc<T> = {_id: string; // The API for the indexDB operation is from the TS-IndexDB NPM toolkit. data: T; expiredAt? : number; }; const isVoid = (data: any) => { if (data === undefined || data === null || Object.is(NaN, data)) { return true; } return false; } class FrontendCache { private dbName = "system"; private tableName = "Cache"; private time = Math.floor(new Date().valueOf() / 1000); constructor() { initDB({ dbName: this.dbName, version: 3, tables: [ { tableName: this.tableName, option: { keyPath: "_id" }, indexes: [ { key: "expiredAt", option: { unique: false } } ] } ] }); setInterval(async () => { try { await getInstance().delete({ tableName: this.tableName, condition: (data: any) => { return data["expiredAt"] <= this.time; }}); } catch (error) { console.log(error); }}, 60 _000); } async get<T>(id: string) { const doc = await getInstance().queryByPrimaryKey<CacheDoc<T>>({ tableName: this.tableName, value: id }); if (! isVoid(doc? .data) && (! doc.expiredAt || doc.expiredAt > this.time)) { return doc.data; } await getInstance().delete<CacheDoc<T>>({ tableName: this.tableName, condition: (data) => { return data._id === id; }}); return null; } /** * @param id * @param data * @param timeout expiration time */ async set<T>(id: string, data: T, timeout = 0): Promise<void> { const $set: Omit<CacheDoc<T>, "_id"> = { data }; timeout && ($set.expiredAt = this.time + timeout); const result = await getInstance().updateByPrimaryKey<CacheDoc<T>>({ tableName: this.tableName, value: id, handle: (value) => { const _id = value._id; value = { _id, ... $set }; return value; }}); if (! result? ._id) { await getInstance().insert({ tableName: this.tableName, data: { _id: id, ... $set } }); } } async increase(id: string, amount: number): Promise<number> { const result = await getInstance().updateByPrimaryKey<CacheDoc<number>>({ tableName: this.tableName, value: id, handle: (value) => { const preData = value.data; value = { ... value, data: preData + amount }; return value; }}); if (result? ._id) { return this.get<number>(id) as any; } else { await getInstance().insert({ tableName: this.tableName, data: { _id: id, data: amount } }); return amount; } } async delete(id: string): Promise<boolean> { const result: any = await getInstance().delete<CacheDoc<any>>({ tableName: this.tableName, condition: (data) => { return data._id === id; }}); return result? .length ! = = 0; } async pop<T>(id: string): Promise<T> { const result: any = await getInstance().delete<CacheDoc<T>>({ tableName: this.tableName, condition: (data) => { return data._id === id; }}); return (result? .length ! = = 0? (result[0]? .data as T ?? null) : null) as any; }}Copy the code

Regular usage

$cache = new FrontendCache(); Vue.prototype.$cache = new FrontendCache(); // Add data await $cache.set(1, {a:1}); // {_id:1, data:{a:1}} await $cache.set(1,{a:1},5000); {_id:1, data:{a:1}, expiredAt: 1625320214} // Get data await $cache.get(1); // {a:1} // delete data await $cache.delete(1) // return true or false // delete data and display deleted data await $cache.pop(1) // {a:1} and delete data Increment a number or add the string await $cache.set(1,200); Await $cache. Happens (1300); {_id:1, data:500} await $cache.set(1,'A'); await $cache.increase(1,'B'); {_id:1, data:'AB'}Copy the code

If you are interested in the source code, please go to Github to check it.