Vue: @click acionaria outro evento vnode @click.

Criado em 11 set. 2017  ·  4Comentários  ·  Fonte: vuejs/vue

Versão

2.4.2

Link de reprodução

https://jsbin.com/qejofexedo/edit?html , js, output

Passos para reproduzir

veja o link de reprodução.

O que é esperado?

Quando eu clico em Expand is True , então expand para se tornar false . E apenas countA mudou.

O que realmente está acontecendo?

Quando clico em Expand is Ture , nada aconteceu.
countA e countB mudaram.
Acho que quando eu clico, expand mudou para false , mas imediatamente o evento de clique foi acionado. Ele executa outro evento de clique do vnode. Em seguida, a expansão mudou para true .

E mais

  • Se eu renomear o segundo div para outro nome de tag, como p , section , nenhum erro ocorrerá.
  • Se eu mover o evento de clique da tag i para a tag pai div no primeiro div, nenhum erro ocorrerá
bug improvement

Comentários muito úteis

Então, isso acontece porque:

  • O evento de clique interno em <i> dispara, acionando uma primeira atualização em nextTick (microtarefa)
  • A microtarefa é processada antes que o evento borbulhe no div externo . Durante a atualização, um ouvinte de clique é adicionado ao div externo.
  • Como a estrutura do DOM é a mesma, tanto o div externo quanto o elemento interno são reutilizados.
  • O evento finalmente atinge o div externo, aciona o ouvinte adicionado pela primeira atualização, por sua vez acionando uma segunda atualização.

Isso é bastante complicado na correção, e outras libs que utilizam microtarefas para enfileiramento de atualizações também apresentam esse problema (por exemplo, Preact). O React não parece ter esse problema porque usa um sistema de eventos sintético (provavelmente devido a casos extremos como este).

Para contornar isso, você pode simplesmente dar às duas divs externas chaves diferentes para forçá-las a serem substituídas durante as atualizações. Isso evitaria que o evento bubbled fosse captado:

<div class="header" v-if="expand" key="1"> // block 1
  <i @click="expand = false, countA++">Expand is True</i> // element 1
</div>
<div class="expand" v-if="!expand" @click="expand = true, countB++" key="2"> // block 2
  <i>Expand is False</i> // element 2
</div>

Todos 4 comentários

qq20170911-185025
parece normal

Sua reprodução está funcionando conforme o esperado ...

@Kingwl @ yyx990803 Desculpe por isso. Eu testei outros casos e esqueci de mudar de volta.

O código importante é

<div class="header" v-if="expand"> // block 1
  <i @click="expand = false, countA++">Expand is True</i> // element 1
</div>
<div class="expand" v-if="!expand" @click="expand = true, countB++"> // block 2
  <i>Expand is False</i> // element 2
</div>

Existem quatro casos:

  • clique em evento ouvir em block 1 e block2 , funciona bem
  • clique em evento ouvir em element 1 e element 2 , funciona bem
  • clique em evento ouça em block 1 e element 2 , altere expand para verdadeiro está ok. Mas não pode mudar de volta.
  • clique em evento ouça em element 1 e block 2 , não pode alterar expand para falso. Mas pode alterar expand para verdadeiro.

Então, isso acontece porque:

  • O evento de clique interno em <i> dispara, acionando uma primeira atualização em nextTick (microtarefa)
  • A microtarefa é processada antes que o evento borbulhe no div externo . Durante a atualização, um ouvinte de clique é adicionado ao div externo.
  • Como a estrutura do DOM é a mesma, tanto o div externo quanto o elemento interno são reutilizados.
  • O evento finalmente atinge o div externo, aciona o ouvinte adicionado pela primeira atualização, por sua vez acionando uma segunda atualização.

Isso é bastante complicado na correção, e outras libs que utilizam microtarefas para enfileiramento de atualizações também apresentam esse problema (por exemplo, Preact). O React não parece ter esse problema porque usa um sistema de eventos sintético (provavelmente devido a casos extremos como este).

Para contornar isso, você pode simplesmente dar às duas divs externas chaves diferentes para forçá-las a serem substituídas durante as atualizações. Isso evitaria que o evento bubbled fosse captado:

<div class="header" v-if="expand" key="1"> // block 1
  <i @click="expand = false, countA++">Expand is True</i> // element 1
</div>
<div class="expand" v-if="!expand" @click="expand = true, countB++" key="2"> // block 2
  <i>Expand is False</i> // element 2
</div>
Esta página foi útil?
0 / 5 - 0 avaliações