"JavaScript Advanced Programming" Chapter 25 Client Storage Learning Record

"JavaScript Advanced Programming" Chapter 25 Client Storage Learning Record

1. Cookie

  • Used to store session information on the client. This specification requires the server to respond to HTTP requests by sending
    Set-Cookie
    HTTP header contains session information
HTTP/1.1 200 OK Content-type: text/html Set-Cookie: name=value Other-header: other-header-value Copy code
  • This HTTP response will set a cookie named "name" with a value of "value".
  • Both the name and value are URL-encoded when they are sent.
  • The browser will store these session information, and will send them back to the server through HTTP header cookies in each subsequent request
GET/index.jsl HTTP/1.1 Cookie : name=value Other-header: other-header-value Copy code

1. Restrictions

  • Bind to a specific domain
    • After setting, it will be sent to the domain that created it along with the request, which can ensure that the information in the cookie is only available to approved recipients.
  • Cookies can take up limited space
    • No more than 300 cookies
    • 4kb each (4096 bytes)
    • Basically unlimited per domain now

2. The composition of cookies

  • cookie parameters
    • name
      • The name that uniquely identifies the cookie
      • not case sensitive
      • The cookie must be URL-encoded
    • value
      • String value stored in cookie
      • Must be URL encoded
    • area
      • The domain where the cookie is valid
      • All requests sent to this domain will contain the corresponding cookie
      • Can contain or exclude subdomains
      • The default is the domain where the cookie is set
    • path
      • The request URL contains this path to send the cookie to the server
    • expire date
      • Timestamp indicating when the cookie was deleted
      • All cookies will be deleted after the default browser session ends
    • Safety Signs
      • After setting, the cookie will be sent to the server only when using SSL secure connection
  • Use a semicolon and a space in the Set-Cookie header to separate
... Set-Cookie: name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com ... Copy code
  • The only non-name-value pair in the cookie when the security flag is source, only one source is needed
... Set-Cookie: name=value; domain=.wrox.com; path=/; secure ... Copy code

3. Cookies in JavaScript

  • document.cookie
    Returns the string of all valid cookies on the page , separated by semicolons

  • All names and values are URL-encoded, so you must use

    decodeURIComponent()
    decoding

  • When setting the value, it will not overwrite any cookie that exists before, unless the existing cookie is set, and the cookie format is the same as the header format of Set-Cookie

    name = value; expires = expiration_time; path = domain_path; domain = domain_name; secure copy the code
  • Only the name and value of the cookie are required

  • It is better to use encodeURIComponent() to encode the name and value

  • To specify additional information for the created cookie, just append a string of the same format directly after the header of Set-Cookie

  • CookieUtil object

class CookieUtil { static get (name) { let cookieName = ` ${ encodeURIComponent (name)} =` let cookieStart = document .cookie.indexOf(cookieName) let cookieValue = null if (cookieStart> -1 ) { let cookieEnd = document .cookie.indexOf( ";" , cookieStart) if (cookieEnd ==- 1 ) { cookieEnd = document .cookie.length }cookieValue = decodeURIComponent ( document .cookie.substring(cookieStart + cookieName.length, cookieEnd)) } return cookieValue } static set (name, value, expires, path, domain, secure) { let cookieText = ` ${ encodeURIComponent (name)} = ${ encodeURIComponent (value)} ` if (expires instanceOf Date ) { cookieText += `; expires = ${expires} ` } if (path) { cookieText += `; path = ${path} ` } if (domain) { cookieText += `; domain = ${domain} ` } if (secure) { cookieText += "; secure" } document .cookie = cookieText } static unset ( name, path, domain, secure ) { CookieUtil.set(name, "" , new Data( 0 ), path, domain, secure) } } Copy code

4. Child cookie

  • A child cookie is a small piece of data stored in a single cookie, essentially using the value of the cookie to store multiple name/value pairs in a single cookie.
name = name1 = value1 & name2 = value2 & name3 = value3 & name4 = value4 & name5 = value5 duplicated code
  • You do not need to store it as your own name/value pair separately, and can store more structured data under the limit of the number of single-domain cookies.
class SubCookieUtil { static get ( name, subName ) { let subCookies = SubCookieUtil.getAll(name); return subCookies? subCookies[subName]: null ; } static getAll ( name ) { let cookieName = encodeURIComponent (name) + "=" , cookieStart = document .cookie.indexOf(cookieName), cookieValue = null , cookieEnd, subCookies, parts, result = {}; if (cookieStart> -1 ) { cookieEnd = document .cookie.indexOf( ";" , cookieStart); if (cookieEnd ==- 1 ) { cookieEnd = document .cookie.length; }cookieValue = document .cookie.substring(cookieStart + cookieName.length, cookieEnd); if (cookieValue.length> 0 ) { subCookies = cookieValue.split( "&" ); for ( let i = 0 , len = subCookies.length; i <len; i++) { parts = subCookies[i].split( "=" ); result[ decodeURIComponent (parts[ 0 ])] = decodeURIComponent (parts[ 1 ]); } return result; } } return null ; } static set ( name, subName, value, expires, path, domain, secure ) { let subcookies = SubCookieUtil.getAll(name) || {}; subcookies[subName] = value; SubCookieUtil.setAll(name, subcookies, expires, path, domain, secure); } static setAll ( name, subcookies, expires, path, domain, secure ) { let cookieText = encodeURIComponent (name) + "=" , subcookieParts = new Array (), subName; for (subName in subcookies) { if (subName.length> 0 && subcookies.hasOwnProperty(subName)) { subcookieParts.push( ` ${ encodeURIComponent (subName)} = ${ encodeURIComponent (subcookies[subName])} ` ); } } if (cookieParts.length> 0 ) { cookieText += subcookieParts.join( "&" ); if (expires instanceof Date ) { cookieText += `; expires = ${expires.toGMTString()} ` ; } if (path) { cookieText += `; path = ${path} ` ; } if (domain) { cookieText += `; domain = ${domain} ` ; } if (secure) { cookieText += "; secure" ; } } else { cookieText += `; expires = ${( new Date ( 0 )).toGMTString()} ` ; } document .cookie = cookieText; } static unset ( name, subName, path, domain, secure ) { let subcookies = SubCookieUtil.getAll(name); if (subcookies) { delete subcookies[subName]; //Delete SubCookieUtil.setAll(name, subcookies, null , path, domain, secure); } } static unsetAll ( name, path, domain, secure ) { SubCookieUtil.setAll(name, null , new Date ( 0 ), path, domain, secure); } }; Copy code

5. Precautions for using cookies

  • HTTP-only
    The cookie can be set on the browser and the server, but it can only be obtained on the server.
  • Because all cookies are sent by the browser to the server as the request header, storing a large amount of information in the cookie may affect the performance of the browser request for a specific domain.
  • It is best to save only necessary information through cookies as much as possible to avoid performance problems.

2. Web Storage

  • LocalStorage is a permanent storage mechanism, and sessionStorage is a cross-session storage mechanism.

1. Storage type

  • Used to save name-value pair data up to the upper limit.
    • clear()
      Delete all values
    • getItem(name)
      Get the value of a given name
    • key(index)
      Get the name of a given numerical position
    • removeItem(name)
      Delete the name-value pair of the given name
    • setItem(name, value)
      Set the value of the given name
  • Can only store strings

2. The sessionStorage object

  • The sessionStorage object only stores session data, which means that the data will only be stored until the browser is closed.

  • The data stored in sessionStorage is not affected by page refresh and can be restored after the browser crashes and restarts.

  • The sessionStorage object is closely related to the server session, so it cannot be used when running local files.

  • The data stored in the sessionStorage object can only be used by the page where the data was originally stored, and is of limited use in multi-page applications.

  • Can be achieved by using

    setItem()
    Method or directly assign a value to the attribute to add data to it.

    //Use method to store data sessionStorage.setItem( "name" , "Nicholas" ); //Use attributes to store data sessionStorage.book = "Professional JavaScript" ; copy the code
  • Synchronous blocking mode, data will be immediately submitted to the storage. Any data written through Web Storage can be read immediately.

  • can use

    getItem()
    Or directly access the attribute name to get

    //Use method to get data let name = sessionStorage.getItem( "name" ); //Use attributes to get data let book = sessionStorage.book; copy the code
  • You can combine the length attribute or use for-in to traverse all values

  • You can use the delete operator to directly delete object attributes, or you can use

    removeItem()
    method

    //Use delete to delete the value delete sessionStorage.name; //Use the method to delete the value sessionStorage.removeItem( "book" ); copy the code
  • The sessionStorage object should be mainly used to store small pieces of data that are only valid during the session

  • To store data persistently across sessions, you can use localStorage

3. The localStorage object

  • In the mechanism of persistent storage of data in the client session, to access the same localStorage object, the page must come from the same domain (subdomain is not acceptable), and use the same protocol on the same port.
  • The method is the same as sessionStorage.
  • the difference
    • The data stored in localStorage will be retained until deleted via JavaScript or the user clears the browser cache
    • localStorage data is not affected by page refresh, nor will it be lost by closing windows, tabs or restarting the browser

4. Store events

  • When the Storage object changes, the storage event will be triggered on the document.
  • Use properties or setItem() to set a value, use delete or removeItem() to delete a value, and this event is triggered every time clear() is called.
  • Contains attributes
    • domain The domain corresponding to the storage change
    • key The key to be set or deleted
    • The new value that the newValue key is set to, or null if it is deleted
    • The value before the oldValue key change
window .addEventListener( "storage" , ( event )=> { //... }) Copy code

5. Restrictions

  • Depending on the specific browser, it is generally set according to each source (protocol, domain, and port).
  • Most are limited to 5MB per source

3. IndexedDB

  • A scheme for storing structured data in the browser
  • The design of IndexedDB is almost completely asynchronous. These requests are executed asynchronously and produce successful results or errors.

1. Database

  • The biggest difference from traditional databases is that IndexedDB uses object storage instead of tables to store data.

  • IndexedDB database is a set of object storage under a common namespace, similar to the implementation of NoSQL style

  • How to use

    • transfer
      indexedDB.open()
      Pass in the name of the database to be opened
      • Already exists, send a request to open it
      • Does not exist, send a request to create and open this database
      • Return IDBRequest instance
        • Add onerror and onsuccess event handlers to the instance
    let db, request, version = 1 request = indexDB.open( "admin" , version) request.onerror = ( event ) => { alrt( `Failed to open ${event.target.errorCode} ` ) } request.onsuccess = ( event ) => { db = event.target.result } Copy code
    • onsuccess
      In event.target.result on behalf of the database instance
    • onerror
      In event.target.errorCode error code stored that the problem will

2. Object storage

  • A key must be specified when creating an object store, and it must be globally unique
  • If the database does not exist yet,
    open()
    The operation creates a new database and then triggers
    upgradeneeded
    event
  • You can set up a handler for this event and create a database schema in the handler.
  • If the database exists, and you specify an upgraded version number, the upgradeneeded event will be triggered immediately, so you can update the database schema in the event handler
request.onupgradeneeded = ( event ) => { const db = event.target.result; //If it exists, delete the current objectStore. You can do this during testing. //But this will delete the existing data every time the event handler is executed if (db.objectStoreNames.contains( "users" )) { db.deleteObjectStore( "users" ); } db.createObjectStore( "users" , { keyPath : "username" }); }; //keyPath property of the second parameter indicates the attribute name is stored in the object should be used as keys duplicated code

3. Affairs

  • Operations after the database is created must be completed through transactions .
  • by
    transaction()
    Create, whenever you want to read or modify data, you must organize all modification operations through transactions.
the let Transaction = db.transaction () to copy the code
  • When no parameters are specified, read-only access to all object storage in the database
  • The specific way is to specify the name of one or more object stores to be accessed
the let Transaction db.transaction = ( "Users" ); the let Transaction db.transaction = ([ "Users" , "anotherStore" ]); Copy Code
  • Each transaction accesses the data in a read-only manner. To modify the access mode, you can pass in the second parameter
    • readOnly
    • readwrite
    • versionchange
the let Transaction db.transaction = ( "Users" , "ReadWrite" ); duplicated code
  • use
    objectStore()
    Method and pass in the name of the object store to access a specific object store
  • add()
    Add object
  • put()
    Update object
  • get()
    Get the object
  • delete()
    Delete object
  • clear()
    Delete all objects
const transaction = db.transaction( "users" ); const store = transaction.objectStore( "users" ); const request = store.get( "007" ); request.onerror = ( event ) => alrt( "Did not get the object!" ); = request.onsuccess ( Event ) => ALRT (event.target.result.firstName); duplicated code
  • Any number of requests can be completed for a transaction, so the transaction object itself also has event handlers
transaction.onerror = ( event ) => { //The entire transaction was cancelled }; transaction.oncomplete = ( event ) => { //The entire transaction completed successfully }; Copy code

4. Insert the object

  • add()
    or
    put()
    data input.
  • Both of these methods receive a parameter, the object to be stored, and save the object to the object storage.
  • The two methods differ only when a key with the same name already exists in the object store.
  • add()
    Will cause errors, and
    put()
    Will simply rewrite the object.
  • More simply, you can think of add() as inserting new values, and put() as updating values.
//Initialize for ( let user in users) { store.add(user) } Copy code
//users is an array of user data let request, requests = []; for ( let user of users) { request = store.add(user); request.onerror = () => { //Handling errors }; request.onsuccess = () => { //processed successfully }; requests.push(request); } Copy code

5. Query by cursor

  • A transaction can obtain a record through a known key.

  • If you want to get multiple pieces of data, you need to create a cursor in the transaction

  • The cursor is a pointer to the result set.

  • Unlike traditional database queries, the cursor does not collect all results in advance. On the contrary, the cursor points to the first result, and will not actively search for the next data before receiving the instruction

  • use

    openCursor()
    Method to create a cursor.

const transaction = db.transaction( "users" ), store = transaction.objectStore( "users" ), request = store.openCursor(); request.onsuccess = ( event ) => { //processed successfully }; request.onerror = ( event ) => { //handle error }; Copy code
  • event.target.result access to the next record in the object store, save the IDBCursor instance or null
  • IDBCursor instance
    • direction
      • String constant, indicating the forward direction of the cursor and whether it should traverse all repeated values
        • NEXT("next")
        • NEXTUNIQUE("nextunique")
        • PREV("prev")
        • PREVUNIQUE("prevunique")
    • key
      • Object key
    • value
      • Actual object
    • primaryKey
      • The key used by the cursor. May be object key or index key
request.onsuccess = ( event ) => { const cursor = event.target.result; if (cursor) { //Always check the console .log( `Key: ${cursor.key} , Value: ${ JSON .stringify (cursor.value)} ` ); } }; Copy code
  • The cursor can be used to update individual records.
    update()
    The method uses the specified object to update the value corresponding to the current cursor
request.onsuccess = ( event ) => { const cursor = event.target.result; let value, updateRequest; if (cursor) { //Always check if (cursor.key == "foo" ) { value = cursor.value; //Get the current object value.password = "magic!" ; //Update the password updateRequest = cursor.update(value); //Request to save the updated object updateRequest.onsuccess = () => { //processed successfully }; updateRequest.onerror = () => { //Handling errors }; } } }; Copy code
  • Can call
    delelte()
    To delete the record of the cursor position
request.onsuccess = ( event ) => { const cursor = event.target.result; let value, deleteRequest; if (cursor) { //Always check if (cursor.key == "foo" ) { deleteRequest = cursor.delete(); //request to delete the object deleteRequest.onsuccess = () => { //processed successfully }; deleteRequest.onerror = () => { //handle error }; } } }; Copy code
  • If the transaction does not have permission to modify the object store,
    update()
    with
    delete()
    Will throw an error
  • By default, each cursor will only create one request. To create another request, one of the following methods must be called
    • continue(key)
      • Move to the next record in the result set.
      • The key parameter is optional. If no key is specified, the cursor moves to the next record;
      • If specified, the cursor moves to the specified key
    • advance(count)
      • The cursor moves forward by the specified count records
request.onsuccess = (event) => { const cursor = event.target.result; if (cursor) { // console.log(`Key: ${cursor.key}, Value: ${JSON.stringify(cursor.value)}`); cursor.continue(); // } else { console.log("Done!"); } };

6

  • key range

  • IDBKeyRange

    • only()

      • get()
      const onlyRange = IDBKeyRange.only("007")
    • lowerBound()

      // "007" const lowerRange = IDBKeyRange.lowerBound("007"); // "007" const lowerRange = IDBKeyRange.lowerBound("007", true);
    • upperBound()

      // "ace" const upperRange = IDBKeyRange.upperBound("ace"); // "ace" const upperRange = IDBKeyRange.upperBound("ace", true);
    • bound()

      // "007" "ace" const boundRange = IDBKeyRange.bound("007", "ace"); // "007" "ace" const boundRange = IDBKeyRange.bound("007", "ace", true); // "007" "ace" const boundRange = IDBKeyRange.bound("007", "ace", true, true); // "007" "ace" const boundRange = IDBKeyRange.bound("007", "ace", false, true);
  • openCursor()

const store = db.transaction("users").objectStore("users"); const range = IDBKeyRange.bound("007", "ace"); request = store.openCursor(range); request.onsuccess = function (event) { const cursor = event.target.result; if (cursor) { // console.log(`Key: ${cursor.key}, Value: ${JSON.stringify(cursor.value)}`); cursor.continue(); // } else { console.log("Done!"); } };

7

  • openCursor()
    IDBKeyRange
  • continue() advance()
    • next
    • nextunique
    • prev
    • prevunique

8

  • ID
  • createIndex()
const transaction = db.transaction("users"); const store = transaction.objectStore("users"); const index = store.createIndex("username", "username", { unique: true });
  • createIndex
    • unique
      options
  • createIndex()
    IDBIndex
    index()
const transaction = db.transaction("users"); const store = transaction.objectStore("users") const index = store.index("username");
  • Can be used on the index
    openCursor()
    Method to create a new cursor
    • This cursor is the same as calling on the object store
      openCursor(
      The created cursor is exactly the same
    • It's just that the result.key property saves the index key, not the primary key
const transaction = db.transaction( "users" ); const store = transaction.objectStore( "users" ); const index = store.index( "username" ); const request = index.openCursor(); request.onsuccess = ( event ) => { //processed successfully }; Copy code
  • openKeyCursor()
    The method can also create a special cursor on the index and only return the primary key of each record.
    • The parameters received by this method are the same as openCursor().
    • The difference is that event.result.key is the index key, and event.result.value is the primary key instead of the entire record.
const transaction = db.transaction( "users" ); const store = transaction.objectStore( "users" ); const index = store.index( "username" ); const request = index.openKeyCursor(); request.onsuccess = ( event ) => { //Processed successfully //event.result.key is the index key, event.result.value is the primary key }; Copy code
  • get()
    Method passes in the index key to obtain a single record through the index
  • If you want to get only the primary key of a given index key, you can use
    getKey()
    method
  • IDBIndex object properties
    • name index name
    • keyPath call
      createIndex()
      Incoming attribute path
    • The object store corresponding to the objectStore index
    • whether the unique index key is unique (boolean)
  • The object store itself also has an indexNames property, which holds the names of the related indexes.
  • Call on object storage
    deleteIndex()
    Method and pass in the name of the index to delete the index

9. Concurrency issues

  • When opening the database for the first time, add
    onversionchange
    Event handlers are very important.
  • This callback will be executed when the database is opened to a new version on another same-origin tab page.
  • The best response to this incident is to immediately shut down the database in order to complete the version upgrade.
let request, database; request = indexedDB.open( "admin" , 1 ); request.onsuccess = ( event ) => { database = event.target.result; database.onversionchange = () => database.close(); }; Copy code

10. Restrictions

  • Information cannot be shared across domains
  • Each source has a limit on the space that can be stored