Estudos de caso da API de armazenamento na Web



Bom dia amigos

Neste artigo, veremos alguns exemplos de uso da API de armazenamento na Web ou do objeto Storage.

O que exatamente vamos fazer?

  • Aprenda a lembrar o tempo de reprodução do vídeo.
  • Trabalharemos com o formulário de login da página.
  • Vamos escrever a lógica da lista de tarefas.
  • Implementamos bate-papo.
  • Esboce uma cesta de mercadorias.

Então vamos.

Breve revisão


O objeto Storage é usado para armazenar dados no lado do cliente e, nesse sentido, atua como uma alternativa aos cookies. A vantagem do armazenamento é o tamanho do armazenamento (a partir de 5 MB, depende do navegador, quando o limite é excedido, o erro "QUOTA_EXCEEDED_ERR" é gerado) e não há necessidade de acessar o servidor. Uma desvantagem significativa é a segurança: custa um script mal-intencionado para acessar a página e a gravação é perdida. Portanto, é altamente desencorajado armazenar informações confidenciais no Armazenamento.

Para ser honesto, vale a pena notar que hoje existem soluções mais avançadas para armazenar dados no lado do cliente - isto é IndexedDB, Service Workers + Cache API, etc.

Você pode ler sobre os trabalhadores de serviço aqui .

A API de armazenamento na Web inclui localStorage e sessionStorage. A diferença entre eles é o tempo de armazenamento de dados. No localStorage, os dados são armazenados permanentemente até serem excluídos "explicitamente" (nem recarregar a página nem fechá-la resulta na exclusão de dados). O tempo de armazenamento de dados no sessionStorage, como o nome indica, é limitado pela sessão do navegador. Como sessionStorage quase nunca é usado na prática, consideraremos apenas localStorage.

O que você precisa saber sobre o localStorage?


  • Ao usar localStorage, uma representação do objeto Storage é criada.
  • Os dados no localStorage são armazenados como pares de chave / valor.
  • Os dados são armazenados como seqüências de caracteres.
  • Os dados não são classificados, o que às vezes leva à sua mistura (como veremos no exemplo da lista de tarefas).
  • Quando você ativa o modo anônimo ou privado em seu navegador, o uso do armazenamento local pode se tornar impossível (depende do navegador).

localStorage possui os seguintes métodos:

  • Storage.key (n) - nome da chave com índice n
  • Storage.getItem () - obtém o item
  • Storage.setItem () - escreve um item
  • Storage.removeItem () - exclui um item
  • Storage.clear () - limpar armazenamento
  • Storage.length - comprimento do armazenamento (número de elementos - pares chave / valor)

Na especificação, fica assim:

interface Storage {
    readonly attribute unsigned long length;
    DOMString? key(unsigned long index);
    getter DOMString? getItem(DOMString key);
    setter void setItem(DOMString key, DOMString value);
    deleter void removeItem(DOMString key);
    void clear();
};

Os dados são gravados no armazenamento de uma das seguintes maneiras:

localStorage.color = 'deepskyblue'
localStorage[color] = 'deepskyblue'
localStorage.setItem('color', 'deepskyblue') //    

Você pode obter os dados assim:

localStorage.getItem('color')
localStorage['color']

Como iterar sobre chaves de armazenamento e obter valores?


//  1
for (let i = 0; i < localStorage.length; i++) {
    let key = localStorage.key(i)
    console.log(`${key}: ${localStorage.getItem(key)}`)
}

//  2
let keys = Object.keys(localStorage)
for (let key of keys) {
    console.log(`${key}: ${localStorage.getItem(key)}`)
}

Como observamos acima, os dados no armazenamento têm um formato de sequência, portanto, existem algumas dificuldades com a gravação de objetos, que podem ser facilmente resolvidas usando o tandem JSON.stringify () - JSON.parse ():

localStorage.user = {
    name: 'Harry'
}
console.dir(localStorage.user) // [object Object]

localStorage.user = JSON.stringify({
    name: 'Harry'
})
let user = JSON.parse(localStorage.user)
console.dir(user.name) // Harry

Para interagir com o localStorage, existe um armazenamento especial de eventos (armazenamento), que ocorre quando os dados são gravados / excluídos. Possui as seguintes propriedades:

  • chave - chave
  • oldValue - valor antigo
  • newValue - novo valor
  • url - endereço de armazenamento
  • storageArea - o objeto no qual a alteração ocorreu

Na especificação, fica assim:

[Constructor(DOMString type, optional StorageEventInit eventInitDict)]
    interface StorageEvent : Event {
    readonly attribute DOMString? key;
    readonly attribute DOMString? oldValue;
    readonly attribute DOMString? newValue;
    readonly attribute DOMString url;
    readonly attribute Storage? storageArea;
};

dictionary StorageEventInit : EventInit {
    DOMString? key;
    DOMString? oldValue;
    DOMString? newValue;
    DOMString url;
    Storage? storageArea;
};

LocalStorage permite a criação de protótipos?


Storage.prototype.removeItems = function() {
    for (item in arguments) {
        this.removeItem(arguments[item])
    }
}

localStorage.setItem('first', 'some value')
localStorage.setItem('second', 'some value')

localStorage.removeItems('first', 'second')

console.log(localStorage.length) // 0

Como verificar dados no localStorage?


//  1
localStorage.setItem('name', 'Harry')

function isExist(name) {
    return (!!localStorage[name])
}

isExist('name') // true

//  2
function isItemExist(name) {
    return (name in localStorage)
}

isItemExist('name') // true

O navegador localStorage pode ser encontrado aqui:



Chega de palavras, é hora de começar a trabalhar.

Exemplos de uso


Lembre-se do tempo de reprodução do vídeo


window.onload = () => {
    //   <video>
    let video = document.querySelector('video')
    
    //  localStorage   currentTime ( ),    video.currentTime
    if(localStorage.currentTime) {
        video.currentTime = localStorage.currentTime
    }
    
    //    video.currentTime,     localStorage.currentTime
    video.addEventListener('timeupdate', () => localStorage.currentTime = video.currentTime)
}

O resultado é o seguinte:



Inicie o vídeo e pare a reprodução no terceiro segundo, por exemplo. O tempo que deixamos é armazenado em localStorage. Para verificar isso, recarregue ou feche / abra a página. Vemos que o tempo atual de reprodução de vídeo permanece o mesmo.

Codepen

Github

Trabalhamos com o formulário de login


A marcação é assim:

<form>
    Login: <input type="text">
    Password: <input type="text">
    <input type="submit">
</form>

Temos um formulário e três "entradas". Para a senha, usamos <input type = "text"> porque, se você usar o tipo correto (senha), o Chrome tentará salvar os dados inseridos, o que nos impedirá de implementar nossa própria funcionalidade.

JavaScript:

//         
let form = document.querySelector('form')
let login = document.querySelector('input')
let password = document.querySelector('input + input')

//  localStorage  
//     
//    
if (localStorage.length != 0) {
    login.value = localStorage.login
    password.value = localStorage.password
}

//      "submit"
form.addEventListener('submit', () => {
    //      localStorage
    localStorage.login = login.value
    localStorage.password = password.value
    
    //    hello  world     , 
    //       "welcome"  
    if (login.value == 'hello' && password.value == 'world') {
        document.write('welcome')
    }
})

Observe que não "validamos" o formulário. Isso, em particular, permite gravar linhas vazias como nome de usuário e senha.

O resultado é o seguinte:



Apresentamos palavras mágicas.



Os dados são gravados no localStorage e uma saudação é exibida na página.

Codepen

Github

Escrevendo a lógica da lista de tarefas


A marcação é assim:

<input type="text"><button class="add">add task</button><button class="clear">clear storage</button>

<ul></ul>

Temos uma "entrada" para inserir uma tarefa, um botão para adicionar uma tarefa à lista, um botão para limpar a lista e o armazenamento e um contêiner para a lista.

JavaScript:

//      
let input = document.querySelector('input')
input.focus()
//       
let addButton = document.querySelector('.add')
//    
let list = document.querySelector('ul')

//   localStorage  
if (localStorage.length != 0) {
    //     /
    for (let i = 0; i < localStorage.length; i++) {
        let key = localStorage.key(i)
        //   -  
        let template = `${localStorage.getItem(key)}`
        //    
        list.insertAdjacentHTML('afterbegin', template)
    }
    
    //      "close" -    
    document.querySelectorAll('.close').forEach(b => {
        //   
        b.addEventListener('click', e => {
            //    "li"
            let item = e.target.parentElement
            //    
            list.removeChild(item)
            //    localStorage
            localStorage.removeItem(`${item.dataset.id}`)
        })
    })
}

//       "Enter"
window.addEventListener('keydown', e => {
    if (e.keyCode == 13) addButton.click()
})

//           ""
addButton.addEventListener('click', () => {
    //   - 
    let text = input.value
    //  ,          "data-id"
    let template = `<li data-id="${++localStorage.length}"><button class="close">V</button><time>${new Date().toLocaleDateString()}</time> <p>${text}</p></li>`
    //   -   
    list.insertAdjacentHTML('afterbegin', template)
    //    localStorage
    localStorage.setItem(`${++localStorage.length}`, template)
    //   
    input.value = ''

    //           ""
    document.querySelector('.close').addEventListener('click', e => {
        //    -   
        let item = e.target.parentElement
        //    
        list.removeChild(item)
        //    localStorage
        localStorage.removeItem(`${item.dataset.id}`)
    })
})

//        ""
document.querySelector('.clear').onclick = () => {
    //  
    localStorage.clear()
    //    
    document.querySelectorAll('li').forEach(item => list.removeChild(item))
    //   
    input.focus()
}

O resultado é o seguinte: As



tarefas adicionadas à lista são armazenadas no localStorage como marcação pronta. Quando a página é recarregada, a lista é formada a partir dos dados do armazenamento (há mixagem, que foi mencionada acima).



Remover uma tarefa da lista clicando na marca de seleção verde remove o par de chave / valor correspondente do repositório.

Codepen

Github

Implementação de bate-papo


A marcação é assim:

<input type="text">
<button class="send">send message</button>
<button class="save">save chat</button>
<button class="clear">clear chat</button>

<div></div>

Temos uma entrada para inserir uma mensagem, três botões: para enviar uma mensagem, salvar correspondência e limpar bate-papo e armazenamento, além de um contêiner para mensagens.

JavaScript:

//      
let input = document.querySelector('input')
input.focus()

//  
let sendButton = document.querySelector('.send')
let saveButton = document.querySelector('.save')
let clearButton = document.querySelector('.clear')

//  
let box = document.querySelector('div')

//      "messages"
if (localStorage.messages) {
    //  
    localStorage.messages
        .split('</p>,')
        .map(p => box.insertAdjacentHTML('beforeend', p))
}

//   
sendButton.addEventListener('click', () => {
    //   
    let text = document.querySelector('input').value
    //  
    let template = `<p><time>${new Date().toLocaleTimeString()}</time> ${text}</p>`
    //    
    box.insertAdjacentHTML('afterbegin', template)
    //   
    input.value = ''
    //    
    localStorage.message = template
})

//       "Enter"
window.addEventListener('keydown', e => {
    if (e.keyCode == 13) sendButton.click()
})

//   "storage"
window.addEventListener('storage', event => {
    //     "messages"
    //  
    if (event.key == 'messages') return
    //      null
    //     
    //     
    event.newValue == null ? clearButton.click() : box.insertAdjacentHTML('afterbegin', event.newValue)
})

//  
saveButton.addEventListener('click', () => {
    //  
    let messages = []
    //  
    document.querySelectorAll('p').forEach(p => messages.push(p.outerHTML))
    //    
    localStorage.messages = messages
})

//    
clearButton.addEventListener('click', () => {
    localStorage.clear()
    document.querySelectorAll('p').forEach(p => box.removeChild(p))
    input.focus()
})

O resultado é o seguinte: A



mensagem que está sendo enviada é gravada em localStorage.message. O evento "storage" permite que você organize a troca de mensagens entre as guias do navegador.



Quando um bate-papo é salvo, todas as mensagens são gravadas em localStorage.messages. Quando a página é recarregada, a correspondência é formada a partir das mensagens gravadas.

Codepen

Github

Layout do carrinho de compras


Não pretendemos criar uma cesta totalmente funcional; portanto, o código será escrito "no estilo antigo".

O layout de um produto fica assim:

<div class="item">
    <h3 class="title">Item1</h3>
    <img src="http://placeimg.com/150/200/tech" alt="#">
    <p>Price: <span class="price">1000</span></p>
    <button class="add" data-id="1">Buy</button>
</div>

Temos um contêiner para as mercadorias, o nome, a imagem e o preço das mercadorias, além de um botão para adicionar mercadorias à cesta.

Também temos um contêiner para botões para exibir o conteúdo da cesta e esvaziar a cesta e o armazenamento e um contêiner para a cesta.

<div class="buttons">
    <button id="open">Cart</button>
    <button id="clear">Clear</button>
</div>
<div id="content"></div>

JavaScript:

//    
let itemBox = document.querySelectorAll('.item'),
    cart = document.getElementById('content');

//    localStorage
function getCartData() {
    return JSON.parse(localStorage.getItem('cart'));
}

//    
function setCartData(o) {
    localStorage.setItem('cart', JSON.stringify(o));
}

//    
function addToCart() {
    //       
    this.disabled = true;
    //        ,   
    let cartData = getCartData() || {},
        //    "Buy"
        parentBox = this.parentNode,
        // id 
        itemId = this.getAttribute('data-id'),
        //  
        itemTitle = parentBox.querySelector('.title').innerHTML,
        //  
        itemPrice = parentBox.querySelector('.price').innerHTML;
    // +1  
    if (cartData.hasOwnProperty(itemId)) {
        cartData[itemId][2] += 1;
    } else {
        // + 
        cartData[itemId] = [itemTitle, itemPrice, 1];
    }
    //    localStorage
    if (!setCartData(cartData)) {
        //   
        this.disabled = false;
    }
}

//    ""    "Buy"
for (let i = 0; i < itemBox.length; i++) {
    itemBox[i].querySelector('.add').addEventListener('click', addToCart)
}

//  
function openCart() {
    //    
    let cartData = getCartData(),
        totalItems = '',
        totalGoods = 0,
        totalPrice = 0;
    //    
    if (cartData !== null) {
        totalItems = '<table><tr><th>Name</th><th>Price</th><th>Amount</th></tr>';
        for (let items in cartData) {
            totalItems += '<tr>';
            for (let i = 0; i < cartData[items].length; i++) {
                totalItems += '<td>' + cartData[items][i] + '</td>';
            }
            totalItems += '</tr>';
            totalGoods += cartData[items][2];
            totalPrice += cartData[items][1] * cartData[items][2];
        }
        totalItems += '</table>';
        cart.innerHTML = totalItems;
        cart.append(document.createElement('p').innerHTML = 'Goods: ' + totalGoods + '. Price: ' + totalPrice);
    } else {
        //    
        cart.innerHTML = 'empty';
    }
}
//  
document.getElementById('open').addEventListener('click', openCart);

//  
document.getElementById('clear').addEventListener('click', () => {
    localStorage.removeItem('cart');
    cart.innerHTML = 'leared';
});

O resultado é o seguinte:



Os produtos selecionados são registrados na loja como um único par de chave / valor.



Quando você clica no botão Carrinho, os dados do localStorage são exibidos em uma tabela, o número total de mercadorias e seu valor são calculados.

Codepen

Github

Obrigado por sua atenção.

All Articles