Реагируют ли методы в Vue?

9

Некоторое время я использовал Vue, и мой опыт всегда заключался в том, что метод будет пересчитываться, если обновляются базовые реактивные данные. Я столкнулся с противоречивой информацией о SO:

  • Я пытался ответить на этот вопрос , и мне неоднократно говорили, что это не так.
  • Принятый ответ здесь означает, что «[метод] будет оцениваться только при явном вызове».

Я просмотрел документы и не увидел ничего невероятно ясного.

Если они не реагируют, то почему этот пример работает?

<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);
    },
  },
};

Я действительно хотел бы получить окончательный и удовлетворительный ответ, на который может сослаться это сообщество.

Дэвид Уэлдон
источник
Отличный вопрос - не уверен, что есть «официальный» ответ, который представляет реальность!
Тайлер

Ответы:

4

Основываясь на том, как отслеживаются изменения из документов, вот что происходит:

Диаграмма цикла реактивности Vue

  1. Для экземпляра компонента создается специальный наблюдатель, чтобы определить, когда требуется повторная визуализация.

  2. Vue преобразует все свойства dataв геттеры и сеттеры.

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. Шаблон представлен. Во время рендеринга выполняется вызов isAwesomeиз шаблона.
  2. В теле isAwesomeвызывателя для awesomeAnimalIdsвызывается.
  3. Наблюдатель устанавливает зависимость от awesomeAnimalIdsобласти data.
  4. После истечения времени ожидания awesomeAnimalIdsобновляется, что вызывает awesomeAnimalIdsустановщик.
  5. Поскольку шаблон зависит от dataполя, получившего уведомление, повторная визуализация запускается.
  6. Повторите шаг (3).

Из этого и приведенного выше примера можно сделать следующие выводы:

Вызов метода, сделанный из шаблона, устанавливает реактивную зависимость от подмножества dataполей, используемых в стеке вызовов метода. Если базовые поля будут обновлены, это вызовет повторную визуализацию компонента.

Существует распространенное заблуждение, что методы «вызываются только один раз» или «запускаются и забываются» при вызове из шаблона. Это явно не всегда так, потому что методы могут установить реактивную зависимость .

Так, когда мы должны использовать вычисляемое свойство против метода?

См. Раздел руководства по компьютерному кешированию и методам . Вот мой взгляд на это:

  • Вычисленное свойство будет переоценено только тогда, когда его реактивные зависимости изменились. Т.е. он использует кеширование для повышения эффективности.
  • Вычисленные свойства не должны иметь побочных эффектов. Например, вы не должны звонитьfetch от них.
  • Всегда предпочитайте вычисляемое свойство методу, если это возможно по соображениям эффективности.
  • Используйте метод, если у вас есть побочные эффекты или если вам нужно передать аргумент (как видно из исходного вопроса).
Дэвид Уэлдон
источник
Хорошее четкое объяснение, спасибо.
Акройдд
4

Нет, методы не являются реактивными. Только данные могут быть реактивными в Vue.

НО важно понять, как работает Vue ...

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

Неважно, если вы ссылаетесь на элемент данных напрямую, используете его в computedили в method. Если данные будут «затронуты» во время рендеринга, изменение данных вызовет повторный рендеринг в будущем ...

Михал Леви
источник
1

Это очень интересный случай.

Из того, что я прочитал и из своего опыта, я могу сказать, что: нет, методы по своей природе не реактивны. Метод должен быть явно вызван для его выполнения.

Но тогда как я могу объяснить ваш случай? Я поместил ваш код в песочницу и, конечно же, по мере того, как вы вставляете id в массив, шаблон обновляется для отображения имени животного. Это указывает на некоторую реактивность. Что дает?

Ну, я провел эксперимент. Я добавил простой divк каждому циклу, который генерирует случайное число при генерации.

<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();
}

И я увидел, что каждый раз, когда в массив помещается новый идентификатор, все случайные числа меняются. Это ключ к пониманию того, почему «кажется», что метод isAwesomeреактивный.

Каким-то образом, когда новый идентификатор помещается в массив, Vue заново визуализирует цикл полностью, следовательно, снова выполняет методы. Я не могу объяснить внутреннюю работу того, почему vue перерисовывает весь цикл, что потребовало бы дальнейших исследований.

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

Т. Шорт
источник
1
это верно для любого наблюдаемого свойства в компоненте (prop / data / computed). всякий раз , когда они изменили это триггера , updateкоторые вызывают re-renderи вызывают какие - либо методы, которые связаны в шаблоне
Ohgodwhy