Storage APIs

Cookies, LocalStorage, IndexedDB

Гоголев Сергей

Cookies: Создание


document.cookie = 'name=Sergey';

document.cookie = 'age=30';

document.cookie = 'name=' + encodeURIComponent('Сергей');
                            %D0%A1%D0%B5%D1%D0%B3%D0%B5%D0%B9

document.cookie = 'age=30';
    

Cookies: Уникальность

Ключ = name + path + domain

document.cookie = 'age=30; path=/';

document.cookie = 'age=30; path=/forum';

document.cookie = 'age=30; domain=example.org';
    

Cookies: domain


document.cookie = 'age=30; domain=beta.example.org';
    
GET /blog
Host: beta.example.org

GET /blog
Host: example.org

GET /blog
Host: old.example.org

    

Cookies: path


document.cookie = 'age=30; path=/blog';
    
GET /blog

GET /blog/list

GET /blog/page/2

GET /bloglist
    

Cookies: Устаревание


document.cookie = 'name=Sergey;' +
    'expires=Tue, 19 Apr 2016 00:00:00 GMT';
    

Cookies: Удаление

Устанавливаем дату устаревания в прошлом


document.cookie = 'name=Sergey;' +
    'expires=Tue, 19 Apr 1970 00:00:00 GMT';
    

Cookies: Чтение


document.cookie = 'age=30; path=/';

document.cookie = 'age=30; path=/forum';

console.log(document.cookie);


age=30; age=30
/forum  /
    
js-cookie

Cookies.set('name', 'Sergey', { expires: 7, path: '' });

Cookies.get('name');

Cookies.remove('name');
    

Cookies: Отправка на сервер


GET /forum HTTP/1.1

Host: example.org
Cookie: name=Sergey; age=30
    

var express = require('express')
var app = express();

app.use(require('cookie-parser')())

app.use((res, req) => {
    const cookies = req.cookies;
});
    

Cookies: Получение с сервера


var express = require('express')
var app = express();

app.use((res, req) => {
    res.cookie('age', 30, {
        path: '/forum'
    })
});
    

HTTP/1.1 200 OK

Set-Cookie: age=30; path=/forum
    

HTTP-only cookies


var express = require('express')
var app = express();

app.use((res, req) => {
    res.cookie('age', 30, {
        httpOnly: true
    })
});
    

HTTP/1.1 200 OK

Set-Cookie: age=30; path=/forum; HttpOnly

Не доступны в js-скриптах на клиенте

Secure cookies


var express = require('express')
var app = express();

app.use((res, req) => {
    res.cookie('age', 30, {
        secure: true
    })
});
    

HTTP/1.1 200 OK

Set-Cookie: age=30; path=/forum; secure

Не доступны по HTTP

Cookies: Особенности

Устаревание из коробки

Доступ с сервера из коробки

4kb

Передаются с каждым запросом

Cookies: Best practice

Cookieless домены для статики (CDN)

Храните id, а не полноценные данные

Обфусцируйте (01100101)

Cookies: Chrome Dev Tools

Cookies: Применение

Инициализация состояния клиента

Авторизация

tools.ietf.org/html/rfc6265

WebStorage: Ограничение

10MB

5MB (Safari, iOS Safari)

2MB (Android Browser)

LocalStorage vs SessionStorage

SessionStorage – хранит данные до окончании сессии (закрытие вкладки)

LocalStorage – хранит данные перманентно, пока скрипт или пользователь не удалит их

LocalStorage: Хранение


IE: ../DOMStore/YPHP6VDO/www.bing[0].xml

Firefox: %profile/webappsstore.sqlite

Chrome: User Data/Default/Local Storage/http_vk.com_0.localstorage
    

LocalStorage: api


localStorage.setItem('name', 'Sergey');

localStorage.getItem('name');

localStorage.removeItem('name')

localStorage.clear();

localStorage.name = 'Sergey';
    

LocalStorage: api


localStorage.obj = JSON.stringify({ name: 'Sergey' });

JSON.parse(localStorage.obj); // { name: 'Sergey' }
    

LocalStorage: api


window.addEventListener('storage', function (event) {
   console.log(event);
});
    

{
    key: 'name',
    oldValue: 'Sergey',
    newValue: 'Sergey Gogoleff'
}
    

Простое общение между окнами

LocalStorage: Переполнение

QUOTA_EXCEEDED_ERROR

LocalStorage: Определение поддержки

Можно:
localStorage in window
    
Но лучше:

try {
    localStorage.setItem('key', 'value');
    localStorage.removeItem('key');
} catch (error) {
    console.error(error);

    document.cookie = 'key=value';
}
    

Private Browsing mode:
Error: SecurityError: DOM Exception 18

Ещё лучше:
Modernizr


Modernizr.localstorage

Modernizr.cookies

Modernizr.audio

Modernizr.flash
    


    

LocalStorage: Поддержка

LocalStorage: Сhrome Dev Tools

LocalStorage: Применение

Хранение настроек

Хранение промежуточных данных

Кеширование

LocalStorage: Особенности

10MB

Не передаёт данные на сервер

Сессионное хранилище из коробки

Общение между окнами

Строго ограничен доменом и схемой

Синхронный

html.spec.whatwg.org/multipage/webstorage.html

WebSQL

Асинхронный интерфейс к SQLite базе

WebSQL: api


var db = window.openDatabase('db', '1', 'database', 32768);

db.transaction(function (t) {
    transaction.executeSql(`
        create table if not exists notes(
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT,
            created DATE
        )
    `);

    transaction.executeSql(`
        insert into notes(title, created)
        values("${title}, "${Date.now}")
    `);
}, onError, onSuccess);
    

IndexedDB

IndexedDB: Особенности

Нет ограничений на размер*

Асинхронная

Не реляционная, а key-object

Не SQL, а API

Строго ограничен доменом и схемой

IndexedDB: Database


var request = window.indexedDB.open('db', 3);

request.onerror = function(event) {
    console.log(event.target.errorCode);
};

request.onsuccess = function(event) {
    var db = event.target.result;
};

    

IndexedDB: Object Store

request.onupgradeneeded = function (event) {
    var db = event.target.result;

    if (!db.objectStoreNames.contains('notes')) {
        var store = db.createObjectStore('notes', {
            keyPath: 'id',
            autoIncrement: true
        });

        store
            .createIndex(
                'name, public', ['name', 'public'],
                {unique: false}
            ); // Для запросов с «where»
    }
}
    

IndexedDB: Add


var transaction = db.transaction(['notes'], 'readwrite');
var store = transaction.objectStore('notes');

var note = {
    id: 'films'
    title: 'Films',
    public: true
}

var request = store.add(note);

request.onerror = function (e) {}
request.onsuccess = function (e) {}

transaction.abort();
    

IndexedDB: Get


var transaction = db.transaction(['notes'], 'readonly');
var store = transaction.objectStore('notes');

var request = store.get('films')

request.onsuccess = function (e) {}

    

IndexedDB: Get all


var transaction = db.transaction(['notes'], 'readonly');
var store = transaction.objectStore('notes');

var cursor = store.openCursor();

cursor.onsuccess = function(event) {
    var cursor = event.target.result;

    if (cursor) {
        console.log(cursor.key);
        console.dir(cursor.value);

        cursor.continue();
    }
}

    

IndexedDB: Where


var transaction = db.transaction(['notes'], 'readonly');
var store = transaction.objectStore('notes');

// store
//    .createIndex(
//        'name, public', ['name', 'public'],
//        {unique: false}
//    );


var cursor = store
    .index('name, public')
    .openCursor(IDBKeyRange.only(['films', true]));
    

IndexedDB: Производительность

dexie.org

var db = new Dexie('MyDatabase');

db.version(1).stores({
    notes: 'name, text'
});

db.open().catch(function (error){});

db
    .notes
    .where('name')
    .equals(['Films'])
    .each(function (note){
        console.log(note.name);
    });
    

Storage APIs

HTTP cookies explained

Web Storage API

Using IndexedDB

Html5 Local Storage How-To

Client-side Data Storage