Os métodos no Vue são reativos?

9

Uso o Vue há algum tempo, e minha experiência sempre foi um método que será recalculado se os dados reativos subjacentes forem atualizados. Encontrei informações conflitantes no SO:

  • Eu estava tentando responder a essa pergunta e disseram várias vezes que esse não era o caso.
  • A resposta aceita aqui indica que "[um método] só será avaliado quando você o chamar explicitamente".

Eu procurei nos documentos e não vi nada incrivelmente claro.

Se eles não são reativos, por que esse exemplo funciona?

<ul>
  <li v-for="animal in animals" :key="animal.id">
    <span v-if="isAwesome(animal)">{{ animal.name }}</span>
  </li>
</ul>
export default {
  data() {
    return {
      awesomeAnimalIds: [],
      animals: [
        { id: 1, name: 'dog' },
        { id: 5, name: 'cat' },
        { id: 9, name: 'fish' },
      ],
    };
  },
  created() {
    setTimeout(() => {
      this.awesomeAnimalIds.push(5);
    }, 1000);
    setTimeout(() => {
      this.awesomeAnimalIds.push(9);
    }, 2000);
  },
  methods: {
    isAwesome(animal) {
      return this.awesomeAnimalIds.includes(animal.id);
    },
  },
};

Eu realmente gostaria de ter uma resposta definitiva e satisfatória a que esta comunidade possa se referir.

David Weldon
fonte
Ótima pergunta - não tenho certeza se existe uma resposta "oficial" que represente a realidade!
Tyler

Respostas:

4

Com base em Como as alterações são rastreadas nos documentos, veja o que está acontecendo:

Diagrama do ciclo de reatividade do Vue

  1. Um inspetor especial é criado para a instância do componente para determinar quando uma nova renderização é necessária.

  2. O Vue converte todas as propriedades de datapara getters e setters.

get animals() {
  // Add dependency: potentially trigger a re-render if animals updates
  ...
}
set animals() {
  // Notify the watcher that animals has been updated
  ...
}
get awesomeAnimalIds() {
  // Add dependency: potentially trigger a re-render if awesomeAnimalIds updates
  ...
}
set awesomeAnimalIds() {
  // Notify the watcher that awesomeAnimalIds has been updated
  ...
}
  1. O modelo é renderizado. Durante a renderização, isAwesomeé feita uma chamada para o modelo.
  2. No corpo de isAwesome, o getter para awesomeAnimalIdsé invocado.
  3. O observador estabelece uma dependência do awesomeAnimalIds campo de data.
  4. Após um tempo limite, awesomeAnimalIds é atualizado, o que chama o awesomeAnimalIdssetter.
  5. Como o modelo depende de um datacampo que recebeu uma notificação, uma nova renderização é acionada.
  6. Repita o passo (3).

A partir deste e deste exemplo acima, podemos concluir o seguinte:

Uma chamada de método feita a partir de um modelo estabelece uma dependência reativa no subconjunto de data campos usado na pilha de chamadas de método. Se os campos subjacentes forem atualizados, ele disparará uma nova renderização do componente.

Existe um equívoco comum de que os métodos são "invocados apenas uma vez" ou "acionam e esquecem" quando chamados a partir de um modelo. Claramente nem sempre é esse o caso, porque métodos podem estabelecer uma dependência reativa .

Então, quando devemos usar uma propriedade computada versus um método?

Consulte a seção do guia sobre Armazenamento em cache versus métodos . Aqui está a minha opinião:

  • Uma propriedade computada será reavaliada somente quando suas dependências reativas forem alteradas. Ou seja, ele usa cache para melhorar a eficiência.
  • As propriedades computadas devem ser livres de efeitos colaterais. Por exemplo, você não deve ligar fetchdeles.
  • Sempre prefira uma propriedade computada a um método, se possível por razões de eficiência.
  • Use um método se você tiver efeitos colaterais ou se precisar passar um argumento (conforme visto na pergunta original).
David Weldon
fonte
Boa explicação clara, obrigado.
Ackroydd
4

Não, os métodos não são reativos. Somente dados podem ser reativos no Vue.

MAS é importante entender como o Vue funciona ...

  1. O Vue pega seu modelo, o compila na função render e executa a função render
  2. Enquanto a função de renderização está em execução, o Vue monitora todos os data()membros (faz isso o tempo todo, não apenas durante a 1ª renderização). Se algum dos dados for acessado durante a renderização, o Vue saberá que o conteúdo desse membro de dados influencia o resultado da renderização.
  3. Vue use o conhecimento reunido na etapa 2. Sempre que o membro de dados "tocou" durante a renderização, ele sabe que a nova renderização deve acontecer e fazer a coisa ...

Não importa se você referenciar o membro de dados diretamente, usá-lo em computedou em um method. Se os dados forem "tocados" durante a renderização, a alteração dos dados acionará a nova renderização no futuro ...

Michal Levý
fonte
1

Este é um caso muito interessante.

Pelo que li e pela minha experiência, posso dizer que: Não, os métodos não são inerentemente reativos. Um método deve ser explicitamente chamado para sua execução.

Mas, como posso explicar seu caso? Coloquei seu código em uma caixa de areia e, com certeza, conforme você introduz os IDs na matriz, o modelo é atualizado para exibir o nome do animal. Isso indicaria alguma reatividade. O que da?

Bem, eu fiz um experimento. Eu adicionei um simples diva cada loop que gera um número aleatório quando gerado.

<li v-for="animal in animals" :key="animal.id">
        <div>{{ random() }}</div>
        <span v-if="isAwesome(animal)">{{ animal.name }}</span>
</li>

...

random() {
      return Math.random();
}

E o que eu vi foi que toda vez que um novo ID era inserido na matriz, todos os números aleatórios mudavam. Essa é a chave para entender por que "parece" como se o método isAwesomefosse reativo.

De alguma forma, quando um novo ID é enviado para a matriz, o Vue renderiza novamente o loop completamente, executando os métodos novamente. Não posso explicar o funcionamento interno do porquê o vue renderiza todo o ciclo, o que exigiria mais pesquisas.

Então, para responder sua pergunta. isAwesomenão é reativo, é apenas uma ilusão criada pela re-renderização do loop.

T. Curto
fonte
11
é verdade para qualquer propriedade observável em um componente (prop / data / computed). sempre que são alterados, ele dispara o updateque causa re-render
ae