Que é Vuex Storage Plugin e por quê?

O que é?

Vuex Storage Plugin é um plugin para Vuex para persistir dados de sua store no localStorage, sessionStorage e etc. Ele oferece a configuração convencional na criação do Vuex.Store para o rootState e módulos estáticos, mas também oferece configuração para módulos dinâmicos.

Por que?

Quando precisei armazenar alguns dados da minha store no sessionStorage não encontrei nenhum plugin simples de usar, fácil de aprender e que se encaixa-se à minha aplicação sem grandes alterações. Precisava que o plugin armazena-se apenas alguns atributos de módulos
dinâmicos e não encontrei nada do tipo ou pelo menos a documentação não me ajudou muito.

Instalação

npm install --save vuex-storage-plugin

Nuxt

// nuxt.config.js

...
plugins: [{ src: '~/plugins/vuex-storage-plugin.config.js', ssr: false }]
...
// ~/plugins/vuex-storage-plugin.config.js

import VuexStoragePlugin from 'vuex-storage-plugin';

export default ({store}) => {
  window.onNuxtReady(() => {
    VuexStoragePlugin({...})(store);
  });
}

Configuração

import Vue from 'vue';
import Vuex from 'vuex';
import VuexStoragePlugin from 'vuex-storage-plugin';

Vue.use(Vuex);

// rootState and static modules configuration
const vuexStoragePluginConfig = {
  prefix: 'vuex',
  storage: {},
  removeIfNull: true,
  populate: [],
  afterPopulate: () => {}
};

const store = new Vuex.Store({
  plugins: [new VuexStoragePlugin(vuexStoragePluginConfig)]
});

...

// dynamic modules configuration
store.registerModule('DynamicModule', {
  ...,
  populate: [],
  afterPopulate: () => {}
});
Atributo Tipo Descrição
prefix String, default 'vuex' Prefixo usado na chave do storage, exemplo: "vuex/myAttr"
storage Storage API usada para persistir o estado da aplicação
removeIfNull Boolean, default true Indica se o plugin deve remover o valor do storage ou salvar null
populate Array de String e/ou Object Indica os módulos, atributos e mutations que devem ser persistidos
afterPopulate Function Hook chamado logo após o estado root ou do módulo ser carregado na store

storage

Seguindo a interface de Web Storage API, como localStorage ou sessionStorage.
Os métodos necessários para o plugin são os seguintes:

interface Storage {
  getItem(key: string): string
  setItem(key: string, value: string)
  removeItem(key: string)
}

Dica

Caso queira criar seu Storage personalizado ou fazer um wrapper para uma lib como js-cookie você só precisa implementar os métodos acima, exemplo:

import Cookies from 'js-cookie';

const CookiesWrapper = {
  setItem: Cookies.set,
  getItem: Cookies.get,
  removeItem: Cookies.remove
};

populate

O atributo populate é onde você indicará quais os atributos devem ser persistidos no storage e populados do storage na criação do Vuex.Store ou no registro de um módulo dinâmico.

Há duas maneiras de se configurar este atributo:

  1. Usando Strings que representam o nome dos atributos e das mutations usadas para atualizar seus valores
  2. Usando Objetos complexos

Usando Strings

Quando usando String entendesse que foi usado o mesmo nome do atributo à mutation responsável por sua atualização.

Usando estado estático:









 




















import Vue from 'vue';
import Vuex from 'vuex';
import VuexStoragePlugin from 'vuex-storage-plugin';

Vue.use(Vuex);

const vuexStoragePluginConfig = {
  storage: localStorage,
  populate: ['attr']
};

const store = new Vuex.Store({
  plugins: [new VuexStoragePlugin(vuexStoragePluginConfig)],
  state() {
    return {
      attr: null
    }
  },
  mutations: {
    attr(state, value) {
      state.attr = value;
    }
  }
});

...

store.commit('attr', 'attrs new value');

Usando módulo dinâmico:





























 

















 




import Vue from 'vue';
import Vuex from 'vuex';
import VuexStoragePlugin from 'vuex-storage-plugin';

Vue.use(Vuex);

const vuexStoragePluginConfig = {
  storage: localStorage
};

const store = new Vuex.Store({
  plugins: [new VuexStoragePlugin(vuexStoragePluginConfig)]
});

...

// unnamespaced module
store.registerModule('UnnamedModule', {
  state() {
    return {
      attr: null
    }
  },
  mutations: {
    attr(state, value) {
      state.attr = value;
    }
  },
  populate: ['attr']
});

store.commit('attr', 'unnamed attrs new value');

// namespaced module
store.registerModule('NamedModule', {
  namespaced: true,
  state() {
    return {
      attr: null
    }
  },
  mutations: {
    attr(state, value) {
      state.attr = value;
    }
  },
  populate: ['attr']
});

store.commit('NamedModule/attr', 'named attrs new value');

Atenção

Configuração como String funciona apenas para o state raíz da store e módulos dinâmicos.
Para módulos estáticos é necessário configuração do nome do módulo junto aos nomes do atributo e mutation.

Usando Objetos

Usando Objetos permite que você use atributos e mutations com nomes diferentes, o nome do módulo sendo configurado e também permite que você defina um valor default para o atributo quando o plugin não encontrar o valor do atributo no storage.

Interface do objeto esperado

interface PopulateItem {
  module: string,
  attr: string,
  mutation: string,
  default: any
}
Atributo Descrição
module Nome do módulo da configuração. Não importa se é nomeado ou não, se não for dinâmico tem de colocar o módulo
attr Nome do atributo que deseja persistir no storage
mutation Nome do mutation a ser observado para persistir na store*
default Valor a ser atribuído quando não existe valor no storage, o valor default é null
storage Um storage como na configuração global no caso de você querer usar um storage diferente para um state especifico

Atenção

É persistido no storage o valor passado como parâmetro para a mutation. Caso o valor seja "processado" dentro da mutation, isso não é visível para o plugin.






















 





 
import Vue from 'vue';
import Vuex from 'vuex';
import VuexStoragePlugin from 'vuex-storage-plugin';

Vue.use(Vuex);

const vuexStoragePluginConfig = {
  populate: [{
    attr: 'attr',
    mutation: 'setAttr'
  }]
};

const store = new Vuex.Store({
  plugins: [new VuexStoragePlugin(vuexStoragePluginConfig)],
  state() {
    return {
      attr: null
    }
  },
  mutations: {
    setAttr(state, payload) {
      state.attr = `bacon is ${payload}`;
    }
  }
});

store.commit('setAttr', 'love');

O valor persistido será "love", não "bacon is love"

afterPopulate

Caso você deseje realizar alguma lógica após receber os dados do storage, o afterPopulate tem a proposta de ser um hook chamado após os dados serem restaurados do storage e inseridos no store.

Existem duas assinaturas de método para esse hook:

  1. Estático raíz e módulos dinâmicos não nomeados, executado após criação da store raíz
  2. Módulo dinâmico e nomeado, executado ao registrar um novo módulo
function(rootStore); // static and unnamed modules
function(moduleStore, rootStore); // dynamic modules