如何强制计算属性重新计算?

问题描述 投票:0回答:6

这是我组件的计算属性:

methods: {
  addFavoritePlace(place_id) {
    axios.post('/api/add-favorite-place', { place_id: place_id })
      .then(response => {
        // I need here force command.
      });
  },
},

computed: {
  filteredPlaces: function () {
    var is_open;

    if (this.showOpened) {
      is_open = 'open'
    } else {
      is_open = 'close'
    }

    return this.singlePlaces.filter((j) => {
        return j.name.toLowerCase().match(this.search.toLowerCase();
    });
  }
}

我的标记:

<span @click="addFavoritePlace(place.id)" class="favorite-btn active" v-if="place.is_favorited == true">
  <i class="icon-heart"></i>
</span>

<span @click="addFavoritePlace(place.id)" class="favorite-btn" v-else>
  <i class="icon-heart"></i>
</span>

我需要再次调用compute内部的过滤函数才能激活收藏夹按钮。

vue.js vuejs2 computed-properties vue-reactivity
6个回答
51
投票

如果您想强制更新计算属性并重新计算其值,您可以简单地使用数据属性并在计算函数中提及该属性(您不必使用它,只需存在即可足够了),然后更改该数据属性;这将强制更新计算值。

这是代码:

data() {
  return {
    refreshKey: 0,
    // ...
  }
},
computed: {
  myComputedValue() {
    // just mentioning refreshKey
    this.refreshKey;

    // rest of the function that actualy do the calculations and returns a value
  },
},
methods: {
  myMethod() {
    // the next line would force myComputedValue to update
    this.refreshKey++;

    // the rest of my method
  },
},


5
投票

Vue3

const now = computed(()=> new Date())

//manually update
now.effect.run()

3
投票

这是我使用组合 API 的看法:

useRefreshable.ts

import { ref } from "vue";

export function useRefreshable<T>(getter: () => T): {
  getter(): T;
  refresh(): void;
} {
  const refreshKey = ref(0);

  return {
    getter() {
      refreshKey.value;
      return getter();
    },
    refresh() {
      refreshKey.value++;
    },
  };
}

useRefreshable.test.ts

import { computed, nextTick } from "vue";
import { useRefreshable } from "@/composition/useRefreshable";

describe("useRefreshable", () => {
  test("", async () => {
    let notReactiveValue = 1;

    function getValue() {
      return notReactiveValue;
    }

    const normalComputed = computed(getValue);

    const refreshable = useRefreshable(getValue);
    const refreshableComputed = computed(refreshable.getter);

    expect(normalComputed.value).toEqual(1);
    expect(refreshableComputed.value).toEqual(1);

    notReactiveValue++;
    await nextTick();

    expect(normalComputed.value).toEqual(1);
    expect(refreshableComputed.value).toEqual(1);

    refreshable.refresh();
    await nextTick();

    expect(normalComputed.value).toEqual(1);
    expect(refreshableComputed.value).toEqual(2);
  });
});

您可以用多种不同的方式来包装它,例如:

import { computed, ComputedRef } from "vue";
import { useRefreshable } from "@/composition/useRefreshable";

export function useRefreshableComputed<T>(getter: () => T): {
  readonly comp: ComputedRef<T>;
  refresh(): void;
} {
  const refreshable = useRefreshable(getter);
  return {
    comp: computed(() => refreshable.getter()),
    refresh: refreshable.refresh,
  };
}

3
投票

您不需要手动强制更新计算属性。只要计算属性的相关值发生变化,就会自动重新计算。

在您的情况下,如果您修改

singlePlaces
filteredPlaces
将自动更新。

但是,重要的是要注意 Vue 如何处理数组和对象的反应性,如文档中所述 (https://v2.vuejs.org/v2/guide/reactivity.html#For-Arrays)。

虽然您没有包含添加喜爱地点的特定代码,但您可能需要使用如下内容:

Vue.set(this.singlePlaces, indexOfItem, updatedItem)

这可确保 Vue 正确跟踪数组元素的更改。


2
投票

首先,只需切换类,您就可以节省两倍的事件处理、两倍的 DOM 节点创建以及两倍的重新计算和重绘,而不是使用标记。另外,我不确定在 @click 事件中调用函数是否不会立即触发该函数,以防万一将其包装到匿名函数中。另外,请注意,我不是将

place.id
传递给
place
的整个
addFavoritePlace
对象引用:

<span
    @click="() => addFavoritePlace(place)"
    class="favorite-btn"
    :class="{active: place.is_favorited}">
        <i class="icon-heart"></i>
</span>

其次,部分

filteredPlaces
代码没有意义,因为您设置的变量不在其他地方使用也没有返回,因此垃圾收集器无论如何都会立即杀死它。相反,你可以选择:

computed: {
  filteredPlaces () {
    const search = this.search.toLowerCase()
    return this.singlePlaces.filter((p) => {
        return p.name.toLowerCase().includes(search)
    });
  }
}

最后,你可以只更新

singlePlaces
项目,它们会自动影响filteredPlaces,不需要做任何特殊的事情,如果你在每个
place
中没有 is_favorited 属性,你可以在 init 或
map
上使用它强制 Vue 使用
set
方法跟踪其更新。另外,正如我所说,我将传递整个
place
引用而不是
id
,这样我们就不必在堆栈中再次找到该元素:

methods: {
  async addFavoritePlace (place) {
    await axios.post('/api/add-favorite-place', { place_id: place.id })
    this.$set(place, 'is_favorited', true)
  },
},

2
投票

这是 Vue 3 中的一个简单解决方案:

this._computedWatchers.computedName.run()

其中

computedName
是所需
computed
项目的名称。

© www.soinside.com 2019 - 2024. All rights reserved.