Web APIs


Гоголев Сергей
Воронцов Максим
Семичев Олег

Storage APIs

Cookies

since 1994

Cookies: создание


document.cookie = 'name=Sergey';

document.cookie = 'age=30';

document.cookie = 'name=' + encodeURIComponent('Сергей');
    

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

name + path + domain


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

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

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

document.cookie = 'age=30; domain=forum.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';
    

var cookies = 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

Web Storage

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: api


localStorage.name = 'Sergey';

localStorage.name; // Sergey
    

LocalStorage: api


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

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

LocalStorage: api


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

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

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

LocalStorage: api

QUOTA_EXCEEDED_ERROR

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

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

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

Private Browsing mode:
Error: SecurityError: DOM Exception 18

Ещё лучше:
Modernizr


Modernizr.localstorage

Modernizr.cookies

Modernizr.audio

Modernizr.flash
    


    

LocalStorage: поддержка

LocalStorage: chrome dev tools

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

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

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

Кеширование

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

10MB

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

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

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

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

Синхронный

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

WebSQL

WebSQL: поддержка

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

WebSQL: api


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

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

    t.executeSql(
        'insert into notes(title, created) values(' +
        '"' + title + '","' + Date.now() +'")'
    );
}, onError, onSuccess);
    

IndexedDB

IndexedDB: поддержка

Абстракция над SQLite

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


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

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('public', 'public', { unique: false });
        store.createIndex('name, public', ['name', 'public']);
    }
}

    

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(e) {
    var cursor = e.target.result;
    if (cursor) {
        console.log('Key: ', cursor.key);
        console.dir('Data: ', cursor.value);
        cursor.continue();
    }
}

    

IndexedDB: Where


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

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