如何从状态安全地复制对象,以使视图不直接改变存储

时间:2019-08-21 19:07:08

标签: vue.js vuejs2 vuex

我有一个视图,它是个人资料页面,可帮助用户更新其个人资料信息。我最初使用以下方法从商店中提取数据:

this.$store.state.auth.user

但是,当我使用v-model将其绑定到输入控件时,<input>更改了input事件的Vuex状态属性。根据文档中的解决方案,我更改了控件以使用:value属性显示其内容,并使用Object.assign()将状态数据合并到本地属性中:

user: Object.assign({
    name: ''
    ...
}, this.$store.state.auth.user),

这部分起作用,因为现在保存数据后,看来合并对象中的数据正在引用状态对象,因此,如果用户在销毁组件之前尝试再次更新数据,则会得到:

"Error: [vuex] do not mutate vuex store state outside mutation handlers."

我意识到提交后我可能可以重新初始化本地属性,并且我可以始终运行:

this.$router.go()

但是我有种na的感觉,我犯了一个男生错误。解决此问题的惯用方法是什么?

1 个答案:

答案 0 :(得分:0)

浅拷贝与深拷贝

Object.assign仅创建一个shallow copy,因此您的user副本仍将引用原始user对象的任何嵌套对象值,如下面的演示所示:

const orig = {
  age: 21,
  name: {
    first: 'John',
    last: 'Doe'
  },
  username: 'jdoe_11',
}

const copy = Object.assign({}, orig) // or: const copy = { ...orig }

// both `name` properties refer to the *same* nested object
console.log('copy.name === orig.name =>', copy.name === orig.name) // => true

copy.name.first = 'Bob'
console.log('orig.name.first =>', orig.name.first) // => 'Bob'

如果要制作与原始对象隔离的深层副本,建议使用lodash.cloneDeep之类的实用工具,该实用工具以递归方式复制对象,包括所有嵌套属性:

const orig = {
  age: 21,
  name: {
    first: 'John',
    last: 'Doe'
  },
  username: 'jdoe_11',
}

const copy = _.cloneDeep(orig)
console.log('copy.name === orig.name =>', copy.name === orig.name) // => false

copy.name.first = 'Bob'
console.log('orig.name.first =>', orig.name.first) // => 'John'
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>

Vuex解决方案

Vuex docs讨论了您的方案的解决方案。

  

处理它的“ Vuex方法”是绑定<input>的值,并在inputchange事件上调用操作:

     
<input :value="message" @input="updateMessage">
     
// ...
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},
methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}
     

这是变异处理程序:

     
// ...
mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}
     

双向计算属性

     

诚然,上面的内容比v-model +局部状态要冗长得多,并且我们也失去了v-model的一些有用功能。一种替代方法是使用带有setter的双向计算属性:

     
<input v-model="message">
     
// ...
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}

如果上面推荐的解决方案不可行,您可以最后禁用Vuex的strict mode。请注意,严格模式是一种开发工具,可以防止突变处理程序之外的意外状态更改,因此在禁用它时应格外小心:

// store.js
export default new Vuex.Store({
  // ...
  strict: false // ? disable strict mode
})