Criando o plug-in Desfazer / Refazer do Vuex para o VueJS

imagem


Há muitas vantagens em centralizar o estado do seu aplicativo na loja Vuex. Uma vantagem é que todas as transações são registradas. Isso permite que você use funções convenientes, como depuração de tempo de execução , nas quais é possível alternar entre estados anteriores para separar tarefas da execução.


Neste artigo, mostrarei como criar a função Desfazer / Refazer ainda mais Rollback / Return usando o Vuex, que funciona de maneira semelhante à depuração durante a depuração. Esse recurso pode ser usado em uma variedade de cenários, de formulários complexos a jogos baseados no navegador.


Você pode conferir o código finalizado aqui no Github e experimentar a demonstração neste Codepen . Também criei um plugin como um módulo NPM chamado vuex-undo-redo, se você quiser usá-lo em um projeto.


Nota: este artigo foi originalmente publicado aqui no blog do desenvolvedor do Vue.js 2017/11/13.

Configuração de plugins


Para tornar esse recurso reutilizável, vamos criá-lo como um plugin do Vue. Essa função requer que adicionemos alguns métodos e dados à instância do Vue, portanto estruturamos o plug-in como um mixin.


module.exports = { install(Vue) { Vue.mixin({ // Code goes here }); } }; 

Para usá-lo em um projeto, podemos simplesmente importar o plug-in e conectá-lo:


 import VuexUndoRedo from './plugin.js'; Vue.use(VuexUndoRedo); 

Idéia


O recurso funcionará revertendo a última mutação se o usuário quiser cancelar e, em seguida, reaplicando-a se quiser repeti-la. Como fazemos isso?


Abordagem No. 1


A primeira abordagem possível é tirar "instantâneos" do estado do repositório após cada mutação e colocar o instantâneo em uma matriz. Para desfazer / refazer, podemos obter o instantâneo correto e substituí-lo pelo estado de armazenamento.


O problema com essa abordagem é que o estado do repositório é um objeto JavaScript. Quando você coloca um objeto JavaScript em uma matriz, simplesmente coloca uma referência ao objeto. Uma implementação ingênua, como a seguinte, não funcionará:


 var state = { ... }; var snapshot = []; // Push the first state snapshot.push(state); // Push the second state state.val = "new val"; snapshot.push(state); // Both snapshots are simply a reference to state console.log(snapshot[0] === snapshot[1]); // true 

A abordagem de instantâneo exigirá que você primeiro crie um clone de estado antes de enviar por push. Dado que o estado do Vue se torna reativo adicionando automaticamente as funções get e set, ele não funciona muito bem com a clonagem.


Abordagem No. 2


Outra abordagem possível é registrar cada mutação fixa. Para cancelar, redefinimos a loja para seu estado inicial e, em seguida, executamos as mutações novamente; tudo menos o último. Reembolsar conceito semelhante.


Dado os princípios do Flux, reiniciar mutações do mesmo estado inicial deve idealmente recriar o estado. Como essa é uma abordagem mais limpa que a primeira, vamos continuar.


Registro de mutações


O Vuex oferece um método de API para assinar mutações, que podemos usar para registrá-las. Vamos definir isso para o gancho created . No retorno de chamada, simplesmente colocamos a mutação em uma matriz que pode ser executada mais tarde.


 Vue.mixin({ data() { return { done: [] } }, created() { this.$store.subscribe(mutation => { this.done.push(mutation); } } }); 

Método de reversão


Para cancelar a mutação, limpamos o repositório e reexecutamos todas as mutações, exceto a última. Veja como o código funciona:


  1. Use o método pop array para remover a última mutação.
  2. Limpar o estado da loja usando a mutação especial EMPTY_STATE (explicada abaixo)
  3. Repita cada mutação restante, corrigindo-a novamente na nova loja. Observe que o método de assinatura ainda está ativo durante esse processo, ou seja, cada mutação será adicionada novamente. Exclua-o imediatamente com pop .

 const EMPTY_STATE = 'emptyState'; Vue.mixin({ data() { ... }, created() { ... }, methods() { undo() { this.done.pop(); this.$store.commit(EMPTY_STATE); this.done.forEach(mutation => { this.$store.commit(`${mutation.type}`, mutation.payload); this.done.pop(); }); } } }); 

Loja de limpeza


Sempre que esse plug-in é usado, o desenvolvedor deve implementar uma mutação em seu repositório chamada emptyState. O desafio é retornar a loja ao seu estado original para que esteja pronta para a recuperação do zero.


O desenvolvedor deve fazer isso por conta própria, porque o plug-in que criamos não tem acesso à loja, apenas à instância do Vue. Aqui está um exemplo de implementação:


 new Vuex.Store({ state: { myVal: null }, mutations: { emptyState() { this.replaceState({ myval: null }); } } }); 

Retornando ao nosso plugin, a mutação emptyState não deve ser adicionada à nossa lista de tarefas, pois não queremos confirmar isso novamente durante o processo de reversão. Evite isso com a seguinte lógica:


 Vue.mixin({ data() { ... }, created() { this.$store.subscribe(mutation => { if (mutation.type !== EMPTY_STATE) { this.done.push(mutation); } }); }, methods() { ... } }); 

Método de retorno


Vamos criar uma nova propriedade de dados, undone , que será uma matriz. Quando removemos a última mutação done durante o processo de reversão, a colocamos nesta matriz:


 Vue.mixin({ data() { return { done: [], undone: [] } }, methods: { undo() { this.undone.push(this.done.pop()); ... } } }); 

Agora podemos criar um método de redo que simplesmente pegará a última mutação undone adicionada e a undone novamente.


 methods: { undo() { ... }, redo() { let commit = this.undone.pop(); this.$store.commit(`${commit.type}`, commit.payload); } } 

Sem retorno possível


Se o usuário iniciar o cancelamento uma ou mais vezes e fizer uma nova nova confirmação, o conteúdo de undone será invalidado. Se isso acontecer, devemos esvaziar o undone .


Podemos descobrir novas confirmações de nosso retorno de chamada de assinatura ao adicionar uma confirmação. No entanto, a lógica é complicada, pois o retorno de chamada não tem uma maneira óbvia de descobrir o que é um novo commit e o que desfazer / refazer.


A abordagem mais fácil é definir o sinalizador newMutation. Por padrão, será verdadeiro, mas os métodos de reversão e retorno o definirão temporariamente como falso. Se a mutação estiver configurada como true, o retorno de chamada de subscribe limpará a matriz undone .


 module.exports = { install(Vue) { Vue.mixin({ data() { return { done: [], undone: [], newMutation: true }; }, created() { this.$store.subscribe(mutation => { if (mutation.type !== EMPTY_STATE) { this.done.push(mutation); } if (this.newMutation) { this.undone = []; } }); }, methods: { redo() { let commit = this.undone.pop(); this.newMutation = false; this.$store.commit(`${commit.type}`, commit.payload); this.newMutation = true; }, undo() { this.undone.push(this.done.pop()); this.newMutation = false; this.$store.commit(EMPTY_STATE); this.done.forEach(mutation => { this.$store.commit(`${mutation.type}`, mutation.payload); this.done.pop(); }); this.newMutation = true; } } }); }, } 

A funcionalidade principal agora está completa! Adicione o plugin ao seu próprio projeto ou à minha demonstração para testá-lo.


API pública


Na minha demonstração, você notará que os botões de cancelamento e retorno estão desativados se a funcionalidade deles não for possível no momento. Por exemplo, se ainda não houve confirmações, você obviamente não pode cancelar ou refazer. Um desenvolvedor que usa este plug-in pode querer implementar uma funcionalidade semelhante.


Para habilitar isso, o plug-in pode fornecer duas propriedades calculadas, canUndo e canRedo como parte da API pública. Isso é trivial de implementar:


 module.exports = { install(Vue) { Vue.mixin({ data() { ... }, created() { ... }, methods: { ... }, computed: {}, computed: { canRedo() { return this.undone.length; }, canUndo() { return this.done.length; } }, }); }, } 

Source: https://habr.com/ru/post/pt433986/


All Articles