我们应该使用v-model来修改Vuex存储吗?

50

你好,我是Vue的初学者,遇到了一个困扰我的问题。我在想我们是否应该使用v-model指令来修改vuex store?Vuex表示我们只应通过mutations来修改vuex store,但是v-model使所有事情变得更加容易和简短。(我问这个问题是因为我找不到清晰的答案)

7个回答

94

当在严格模式下使用Vuex时,对于属于Vuex的状态使用v-model可能有些棘手。

处理这个问题的“Vuex方式”是绑定<input>的值并在输入或更改事件上调用一个action。

一定要查看该页面上简单的“双向计算属性”示例:

https://vuex.vuejs.org/guide/forms.html

<input v-model="message">

computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}

这对于消息中更复杂的数据是否有效? - Jan Veselý
3
谢谢你提供的解决方案。我将其修改为使用dispatch("SomeAction")而不是commit,因为使用操作是推荐的方法。 - Christian Johansen

11

我认为另一个好的选择是使用vuex-map-fields,这个选项在任何答案中都没有提到。实际上,库的作者已经为该库的有用性写了一个非常好的解释。根据其GitHub页面,你可以像这样使用该库:

在你的Vuex Store中,你可以有类似于以下的代码段:

import Vue from 'vue';
import Vuex from 'vuex';

import { getField, updateField } from 'vuex-map-fields';

Vue.use(Vuex);

export default new Vuex.Store({
  // ...
  modules: {
    fooModule: {
      namespaced: true,
      state: {
        foo: '',
      },
      getters: {
        getField,
      },
      mutations: {
        updateField,
      },
    },
  },
});

在你的组件代码中,你可以编写类似于以下内容:

<template>
  <div id="app">
    <input v-model="foo">
  </div>
</template>

<script>
import { mapFields } from 'vuex-map-fields';

export default {
  computed: {
    // `fooModule` is the name of the Vuex module.
    ...mapFields('fooModule', ['foo']),
  },
};
</script>

在我回答的第一句话中提供了一个链接到库的GitHub存储库,其中显示了各种用例的其他示例。


8
上述解决方案也可以通过突变来实现:
<template>
  <input v-model="message">
</template>

<script>
import { mapMutations, mapState } from 'vuex';

export default {
  computed: {
    ...mapState({messageFromStore: 'message'}),
    message: {
      get() {
        return this.messageFromStore;
      },
      set(value) {
        this.updateMessage(value);
      }
    }
  },
  methods: {
    ...mapMutations('updateMessage')
  }
};
</script>

1
我想你是指第2行的 <input v-model="tab"> - Kwesi Smart

3

我的解决方案是使用getter设置value,并使用@input调用突变。

<input
  type="text"
  :value="$store.getters.apartmentStreet"
  @input="value => $store.commit('apartmentValue', { handle: 'street', value })"
>

getters.js:

export default {
  apartmentStreet: state => state.apartment.street,
};

mutations.js

export default {
  apartmentValue(state, payload) {
    let oldValue = state.apartment[payload.handle];
    let newValue = payload.value;
    if (newValue !== oldValue) state.apartment[payload.handle] = payload.value;
  }
};

如果您使用此方法,请确保检查您想要的事件。


1
我使用这个解决方案。

data() {
  return {
    formData: {
      username: '',
      email: '',
      bio: {
        firstName: '',
        lastName: ''
      },
      games: ['civ4', 'caesar3', 'homeworld', 'cataclysm'],
         
    }
  }
},
computed: {
  ...mapGetters({   //or mapState
    user: 'users'
  })
},
watch: {
  user(newValue) {
    this.formData.username = newValue.name;
    this.formData.email = newValue.email;
    this.formData.bio.firstName = newValue.bio.firstName;
    this.formData.bio.lastName = newValue.bio.lastName;
    this.formData.games = newValue.games.map(x=> { return x });
  }
},
beforeCreate: fucntion() {
  this.$store.dispatch('getUser');
}

然后您只需定期使用v-model。

重要的是从存储中深度复制对象,就像对数组使用映射一样,并且要像我在内部处理对象时那样进行。

此外,您还需要在存储中初始化此用户对象,并使用空字段。


0

在计算属性中使用getter和setter非常完美。但是在我的情况下,我有一个“重置”按钮,它应该恢复所有未保存的更改。我已经使用了watcher与mapState的组合:

  <h5>Edit User</h5>
  ...
  <q-input v-model="user_data.full_name" label="Full Name" />
  <q-input v-model="user_data.email" type="email" label="Email" />
  <q-checkbox v-model="user_data.is_active" label="Is Active" />
  ...
  <q-btn label="Cancel" @click="cancel" />
  <q-btn label="Reset" @click="reset" />
  <q-btn label="Save" @click="save" />
</template>
<script>
import { mapActions, mapState } from "vuex";
export default {
  data() {
    return {
      id: "",
      user_data: {},
    };
  },
  computed: {
    ...mapState(["user"]),
  },
  watch: {
    user(value) {
      this.user_data = { ...value }; // very important to copy object
    },
  },
  mounted() {
    this.id = parseInt(this.$route.params.id);
    this.getUserById({ id: this.id });
  },
  methods: {
    ...mapActions(["saveUser", "getUserById"]),
    save() {
      this.saveUser(this.user_data);
      this.$router.push({ name: "userList" });
    },
    reset() {
      this.user_data = this.user;
    },
    cancel() {
      this.reset();
      this.$router.push({ name: "userList" });
    },
  },
};
</script>


-2

是的,你可以这样做,但这不是最佳实践。

正如文档所说,为了保持对状态的控制,状态应该仅在mutation内部更新。

但如果你真的想这样做,可以使用以下方法:

v-model="$store.state.yourProperty"

是的,但为什么我们不应该使用v-model(因为我们只能通过mutations修改vuex store),但是v-model正在做什么,以至于不建议使用v-model指令修改vuex store? - Krzysztof Kaczyński
3
这种约定的好处是我们可以记录发生在存储中的所有状态变化,并实现高级调试助手,例如突变日志、快照和历史重放/时间旅行。 - Tristan De Oliveira
1
@TristanDeoliveira 如果有人不关心“高级调试助手”,因为他正在构建一个简单的应用程序,那该怎么办?直接使用v-modeling存储会引起其他问题吗?当我开始学习Vue时,我确实使用了v-models来直接链接存储(我使用getter而不是state对象),我不记得遇到任何问题。对于小型应用程序来说,最佳实践难道不是过度设计吗? - Eggon
1
@Eggon同意,vuex应该在幕后处理这个问题... - Paranoid Android
@Eggon 我认为,如果只是一个简单的应用程序,甚至不值得讨论。另一方面,大型项目有大量的异步调用、事件和监视器。在这种情况下,很难跟踪异常行为,因此保持严格的流程将会得到回报。 - José Henrique

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接