VueJS v-if с методом всегда возвращает false - PullRequest
1 голос
/ 25 марта 2019

Я использую v-for для просмотра своих сообщений.Теперь мне нужно проверить дату каждого сообщения, а если день меняется, отобразить день, добавить его в мои данные и продолжить.Это означает, что каждый день должен отображаться только один раз.

<template v-for="(message, index) in messages">
  <div class="spacer">
    <span class="grouped-date" v-if="displayPostDate(message.created_at)">
        {{ message.created_at }}
    </span>
  </div>
</template>

Странная часть в том, что в моем методе displayPostDate() я могу проверить:

if (!this.datesDone.includes(d)) {}

И это успешно работает, когдаЯ console.log это, но независимо от того, что я возвращаю, Vue никогда не анализирует правильные данные.

Вот displayPostDate():

let d = date.substring(0, 10);

if (!this.datesDone.includes(d)) {

    console.log('not present');
    this.datesDone.push(date.substring(0, 10));

    return true;

} else {

    console.log('present');
    return false;

}

Ответы [ 2 ]

1 голос
/ 26 марта 2019

Вот что происходит ...

У метода displayPostDate есть побочный эффект обновления реактивной зависимости (т. Е. Он обновляет datesDone[]), что вызывает другой цикл рендеринга, который вызываетметод снова.

Например, скажем, у вас есть пустой datesDone[] и четыре элемента в messages[], каждый с уникальными created_at датами.

  1. При первоначальном рендеринге displayPostDate не находит даты created_at в своем datesDone[], поэтому добавляет их в массив.Он также возвращает true для отображения span s.
  2. Vue обнаруживает изменение в datesDone[] - зависимость displayPostDate, поэтому он запускает другой цикл рендеринга.
  3. При втором рендеринге displayPostDate находит все даты в datesDone[], поэтому никаких изменений в datesDone[] не происходит.Он также возвращает false, чтобы скрыть span s.Поскольку зависимость не изменилась, Vue не выполняет повторную визуализацию.

Вот как это исправить ...

Во-первых, обратите внимание, что для вызова метода в шаблоне используется неэффективно , поскольку он вызывается каждый раз при визуализации шаблона, что может происходить несколько раз, что, в свою очередь, требует, чтобы метод был идемпотентным (и не изменял свои реактивные зависимости), чтобы избежать цикла рендеринга.

ToДля решения этой проблемы более простым решением является предварительная фильтрация списка, чтобы он включал только те элементы, которые вы хотите отобразить (вместо условного отображения в шаблоне).Это дает дополнительное преимущество, заключающееся в упрощении шаблона и устранении необходимости в datesDone[], при условии, что он не имеет другого использования, кроме как для отслеживания дубликатов.Я рекомендую создать этот список как вычисляемое свойство , чтобы результат кэшировался и не подвергался ненужной повторной оценке в другом цикле рендеринга:

// script
computed: {
  uniqueMessages() {
    const uniq = this.messages.reduce((c, msg) => {
      c[msg.created_at] = c[msg.created_at] || msg
      return c
    }, {})

    return Object.values(uniq)
  }
}

// template
<div v-for="message in uniqueMessages">
  <span class="grouped-date">
    {{ message.created_at }}
  </span>
</div>

demo

0 голосов
/ 25 марта 2019

Не используйте методы в v-if s - это ошибка (как вы встречали), и это не Vue way (, вот почему ). Вместо этого используйте вычисленные свойства:

  1. Добавить вычисляемое свойство, хранящее массив логических значений
    computed: {
      displayPostDateArr() {
        return this.messages.map(message => this.displayPostDate(message.created_at));
      }
    }
    
  2. Условно отображать дату в вашем шаблоне:
    <template v-for="(message, index) in messages">
        <span
          class="grouped-date"
          v-if="displayPostDateArr[index]">
            {{ message.created_at }}
        </span>
    </template>
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...