如何防止一个由Vue组件调用的方法在每次v-model更改时都运行?

huangapple go评论70阅读模式
英文:

How can I prevent a method called by a Vue component from running every time the v-model changes?

问题

我有一个表单,从一个API中填充了多个下拉列表,所以我创建了一个从该API获取列表的方法(getList())。下拉列表使用Vuetify的v-autocomplete组件构建,它使用items属性来填充列表,我从该属性中调用getList()

我的问题是,所有问题共享一个v-model对象来存储响应,每当v-model更改时,所有自动完成都会再次调用getList()方法来重新填充列表。由于我有多个问题,每个问题都会多次调用API,所以这个表单运行得相当慢。

以下是这个问题的示例:

<template>
  <v-app>
    <v-container>
      <v-text-field label="文本问题" v-model="responses.text"></v-text-field>
      <v-autocomplete
        label="随机问题"
        :items="getList()"
        v-model="responses.autocomplete"
      ></v-autocomplete>

      <p>文本 v-model: {{ responses.text }}</p>
      <p>自动完成 v-model: {{ responses.autocomplete }}</p>
    </v-container>
  </v-app>
</template>

<script>
  export default {
    data() {
      return {
        responses: {},
      };
    },

    methods: {
      getList() {
        console.log('获取列表');
        return ['value1', 'value2', 'value3'];
      },
    },
  };
</script>

请注意,每当您向文本字段添加新字符时,都会调用getList()方法(请查看控制台)。

我的问题是,有没有办法阻止每次v-model更改时都调用getList()方法?

我尝试过的方法:

  • 为每个问题分配自己的v-model,以便它们不会相互影响。不幸的是,这并不起作用。似乎每当数据发生更改时,getList()方法都会被调用。请查看另一个示例:
<template>
  <v-app>
    <v-container>
      <v-text-field label="文本问题" v-model="text"></v-text-field>
      <v-autocomplete
        label="随机问题"
        :items="getList()"
        v-model="autocomplete"
      ></v-autocomplete>

      <p>文本 v-model: {{ text }}</p>
      <p>自动完成 v-model: {{ autocomplete }}</p>
    </v-container>
  </v-app>
</template>

<script>
  export default {
    data() {
      return {
        responses: {},
        text: '',
        autocomplete: '',
      };
    },

    methods: {
      getList() {
        console.log('获取列表');
        return ['value1', 'value2', 'value3'];
      },
    },
  };
</script>
  • mounted钩子中获取列表。虽然这解决了这个问题,但在我的情况下,我不能使用它,因为一些问题的下拉列表依赖于其他问题。例如,如果问题1的响应是A,问题2将获得列表[x,y,z],但如果响应是B,它将获得列表[q,r,s]。因此,我确实需要问题2对问题1做出反应,但我不需要它对问题3做出反应。

所以,您有没有关于通过仅在实际需要时调用getList来提高此组件性能的提示?

英文:

I have a form that populates a number dropdown lists from an API, so I created a method that gets the list (getList()) from that API. The dropdowns are built using the Vuetify v-autocomplete component, which populate the list using the items prop and I am calling getList() from this prop.

My problem is that all of the questions share a v-model object to store the responses and every time the v-model changes, all of the autocompletes call the getList() method again to repopulate the lists. Since I have multiple questions and each is making an API call multiple times, this form is running quite slowly.

Here's an example of this:

&lt;template&gt;
  &lt;v-app&gt;
    &lt;v-container&gt;
      &lt;v-text-field label=&quot;text question&quot; v-model=&quot;responses.text&quot;&gt;&lt;/v-text-field&gt;
      &lt;v-autocomplete
        label=&quot;random question&quot;
        :items=&quot;getList()&quot;
        v-model=&quot;responses.autocomplete&quot;
      &gt;&lt;/v-autocomplete&gt;

      &lt;p&gt;Text v-model: {{ responses.text }}&lt;/p&gt;
      &lt;p&gt;Autocomplete v-model: {{responses.autocomplete}}&lt;/p&gt;
    &lt;/v-container&gt;
  &lt;/v-app&gt;
&lt;/template&gt;

&lt;script&gt;
  export default {
    data() {
      return {
        responses: {},
      }
    },

    methods: {
      getList() {
        console.log(&#39;Getting list&#39;)
        return [&#39;value1&#39;, &#39;value2&#39;, &#39;value3&#39;]
      },
    },
  }
&lt;/script&gt;

Note that every time you add a new character to the text field, the getList() method is called (check the console).

My question is, is there a way to prevent the getList() method from getting called every time the v-model changes?

What I've tried:

  • Giving each question its own v-model so they don't affect each other. Unfortunately this doesn't work. It looks like any time any of the data changes, the getList() method is called. See this other example:
&lt;template&gt;
  &lt;v-app&gt;
    &lt;v-container&gt;
      &lt;v-text-field label=&quot;text question&quot; v-model=&quot;text&quot;&gt;&lt;/v-text-field&gt;
      &lt;v-autocomplete
        label=&quot;random question&quot;
        :items=&quot;getList()&quot;
        v-model=&quot;autocomplete&quot;
      &gt;&lt;/v-autocomplete&gt;

      &lt;p&gt;Text v-model: {{ text }}&lt;/p&gt;
      &lt;p&gt;Autocomplete v-model: {{autocomplete}}&lt;/p&gt;
    &lt;/v-container&gt;
  &lt;/v-app&gt;
&lt;/template&gt;

&lt;script&gt;
  export default {
    data() {
      return {
        responses: {},
        text: &#39;&#39;,
        autocomplete: &#39;&#39;,
      }
    },

    methods: {
      getList() {
        console.log(&#39;Getting list&#39;)
        return [&#39;value1&#39;, &#39;value2&#39;, &#39;value3&#39;]
      },
    },
  }
&lt;/script&gt;
  • Getting the lists in the mounted hook. While this solves this problem, I cannot use it in my case because the dropdown list of some questions are dependent on other questions. For example, if Question 1 gets a response of A, Question 2 will get list [x, y, z], but if the response is B, it will get list [q, r, s]. So I do need Question 2 to react to Qestion 1, but I don't need it to react to Question 3.

So yes, any tips on improving the performance of this component by calling getList only when it's actually needed?

答案1

得分: 1

将当前的列表项存储在数据中,但不要在mounted中设置它,而是通过watcher进行更新。

    watch: {
      'responses.text': {
        handler(current, old) {
          if (!this.needsReload(current, old)){
            return
          }
          this.listItems = this.getList(current)
        },
        immediate: true,
      },
    },

设置immediate:true标志将在设置watcher时触发初始加载。

在你的示例中,你可以使用computed,因为getList()是同步的,但当你调用API时,应该坚持使用watcher,因为computed应该没有副作用

这是更新后的示例,根据文本输入中的第一个字母填充列表。

英文:

As you already did, store the current list items in data, but instead of setting it in mounted, updated it through a watcher.

    watch: {
      &#39;responses.text&#39;: {
        handler(current, old) {
          if (!this.needsReload(current, old)){
            return
          }
          this.listItems = this.getList(current)
        },
        immediate: true,
      },
    },

Setting the immediate:true flag will trigger an initial load when the watcher is set up.

In your example, you could use a computed, since getList() is synchronous, but when you call an API, you should stick to a watcher since computeds should be free of side-effects.

Here is the updated playground, where the list is filled according to the first letter in the text input.

huangapple
  • 本文由 发表于 2023年6月9日 01:26:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76434309.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定