javascript - 突变未在 Vuex 中使用计算属性进行注册

标签 javascript vue.js local-storage vuex

我有一个系统,它使用大量表单组件来根据输入的信息实时更新 View 。当我第一次构建应用程序时,我主要使用 v-model、本地组件数据和 vue 保存状态来将数据保存在本地存储中,以便在重新加载页面时数据得以保留。

然而,当我随着程序扩展而转向 vuex 时,这并不是那么简单,所以我决定使用带有 getter 和 setter 的计算属性的 v-model,这样我就不必编写超过 50 种不同的更改函数输入。我还意识到,您可以为一个对象创建一个计算变量,并且仍然使用 v-model 来访问和更新该对象的属性,如下所示:

  <div v-for="prof in info.profs">
  <textarea v-model="prof.name" class="code-input uk-input" rows="1" cols="20"></textarea>
  <textarea v-model="prof.email" class="code-input uk-input" rows="1" cols="25"></textarea>
  <textarea v-model="prof.office" class="code-input uk-input" rows="1" cols="50"></textarea> <br>
  </div>

info: {
  get () {
    return this.$store.getters.getInfo
  },
  set (payload) {
    this.$store.commit('updateInfo', payload)
  }
},

这工作得很好,商店更新每个属性的数据,而无需创建自己单独的计算变量,但由于某种原因,它不会在 vue chrome 开发工具上显示为提交的突变“updateInfo”,并且当我使用 vuex 的本地存储插件(如 vuex-persistedstate 或 vuex-persist)时,它不会更改本地存储数据,直到我提交另一个正常结构的突变。现在我的解决方法是在组件中创建属性的本地副本,然后观察该属性的更改并提交到商店,这让我可以再次使用组件级别的本地存储混合,但我觉得必须有一个更好的执行此操作的方法不涉及为 info 中的每个属性编写更改函数或计算变量,因为这在此应用程序中会非常冗长。

data () {
return {
  info: this.$store.getters.getInfo
 }
},
watch: {
info: function(payload){
  this.$store.commit('updateInfo', payload)
 }
},

最佳答案

其实这两种方式都是错误的。将 strict: true 添加到您的商店,您将看到在这两种情况下都会抛出错误。

在这两种选择中,profnameemailoffice 属性都被直接修改(这违反了 Vuex 原则,该原则规定每个变化都应该通过突变发生)。

同样,不是计算 setter (第一种情况),也不是观察器(第二种情况)被触发,因为您没有修改 item,而是它的深层嵌套属性(例如 名称)。

<小时/>

最简单的解决方案是放弃 v-model 并使用 :value@input 绑定(bind)。示例:

<textarea :value="prof.name" @input="updateProf(prof, 'name', $event)" >

请注意,它使用 updateProf 方法,该方法提交突变(见下文)并进入 mixin。

这样所有修改都在突变内完成。最后一点,如果您使用 :value@input verbose,您可以创建一个 custom directive来处理它。

JSFiddle link或下面的演示(相同的代码)。

const store = new Vuex.Store({
  strict: true,
  state: {
    info: {
        profs: [
        {name: "Alice", email: "alice@example.com", office: "NY"},
        {name: "Bob", email: "bob@example.com", office: "CA"}
      ]
    }
  },
  mutations: {
    updateProf(state, {prof, prop, value}) {
        prof[prop] = value;
    }
  },
  getters: {
    getInfo: state => {
      return state.info
    }
  }
});
const mixin = {
  computed: {
    info() {
      return this.$store.getters.getInfo
    }
  },
  methods: {
    updateProf(prof, prop, e) {
        this.$store.commit('updateProf', {prof, prop, value: e.target.value})
    }
    }
}
new Vue({
  store,
  mixins: [mixin],
  el: '#app'
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>

<div id="app">
  {{ info }}
  <div v-for="prof in info.profs">
    <hr>
    name: <textarea :value="prof.name" @input="updateProf(prof, 'name', $event)" class="code-input uk-input" rows="1" cols="20"></textarea> <br>
    email: <textarea :value="prof.email" @input="updateProf(prof, 'email', $event)" class="code-input uk-input" rows="1" cols="25"></textarea> <br>
    office: <textarea :value="prof.office" @input="updateProf(prof, 'office', $event)" class="code-input uk-input" rows="1" cols="50"></textarea>
  </div>
</div>

<小时/>

保留v-model

只是为了避免有人告诉我我没有说过这是可能的,这是您可以继续使用 v-model 的一种方法。这个替代方案的关键点是进行深度克隆和深度等于的函数。我提供了两个简单/幼稚的实现,YMMV:

JSFiddle link 。演示(与 fiddle 相同的代码)如下:

const store = new Vuex.Store({
  strict: true,
  state: {
    info: {
        profs: [
        {name: "Alice", email: "alice@example.com", office: "NY"},
        {name: "Bob", email: "bob@example.com", office: "CA"}
      ]
    }
  },
  mutations: {
    updateInfo(state, data) {
        state.info = data
    }
  },
  getters: {
    getInfo: state => {
      return state.info
    }
  }
});

// these two functions are key here
// consider using other implementations if you have more complicated property types, like Dates
function deepClone(o) { return JSON.parse(JSON.stringify(o)); }
function deepEquals(o1, o2) { return JSON.stringify(o1) === JSON.stringify(o2) }

const mixin = {
    data() {
    return {
      info: deepClone(this.$store.getters.getInfo),
    }
  },
  computed: {
    getInfo() {
      return this.$store.getters.getInfo;
    }
  },
  watch: {
    getInfo: {
        deep: true,
        handler(newInfo) {
        if (!deepEquals(newInfo, this.info)) { // condition to prevent infinite loops
                this.info = deepClone(newInfo);
        }
        }
    },
    info: {
        deep: true,
        handler(newInfo) {
            this.$store.commit('updateInfo', deepClone(newInfo))
        }
    }
    }
}
new Vue({
  store,
  mixins: [mixin],
  el: '#app'
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>

<div id="app">
  {{ info }}
  <div v-for="prof in info.profs">
    <hr>
    name: <textarea v-model="prof.name"  class="code-input uk-input" rows="1" cols="20"></textarea> <br>
    email: <textarea v-model="prof.email"  class="code-input uk-input" rows="1" cols="25"></textarea> <br>
    office: <textarea v-model="prof.office"  class="code-input uk-input" rows="1" cols="50"></textarea>
  </div>
</div>

关于javascript - 突变未在 Vuex 中使用计算属性进行注册,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49565655/

相关文章:

javascript - "Autocomplete is not a function"JavaScript错误

javascript - 使用 jquery 在具有 4 行或更多行 (tr) 的表格周围添加边框

javascript - Vue.js - 当子组件在 <transition> 内时无法访问 this.$parent

javascript - Angularjs ngStorage 在其他 Controller /浏览器选项卡中观看本地存储变量

javascript - 在网页中包含本地照片

javascript - 使用 PHP 撰写但不发送电子邮件

php - javascript 数组到 php array_sum()

javascript - Safari 阻止 video.js 播放器自动播放

javascript - 将适用于 Javascript 的 AWS 开发工具包与 Vue.js 2.0 结合使用

javascript - Cookies 或 localstorage 是最好的方法?