未使用 Vuex 中的计算属性注册突变

Posted

技术标签:

【中文标题】未使用 Vuex 中的计算属性注册突变【英文标题】:Mutations not registering using computed properties in Vuex 【发布时间】:2018-09-08 23:27:28 【问题描述】:

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

但是,当我随着程序的扩展而转移到 vuex 时,这并不是那么简单,所以我决定使用 v-model 和使用 getter 和 setter 的计算属性,这样我就不必编写超过 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)时,它不会更改本地存储数据,直到我提交另一个结构正常的突变。现在我的解决方法是在组件中创建属性的本地副本,然后观察该属性的更改并提交到存储,这让我可以再次使用组件级别的 localstorage mixin,但我觉得必须有一个更好的这样做的方法不涉及为 info 中的每个属性编写更改函数或计算变量,因为这在此应用程序中会非常冗长。

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

【问题讨论】:

【参考方案1】:

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

在这两种替代方案中,profnameemailoffice 属性都被直接修改(这违反了 Vuex 原则,即每次更改都应通过突变发生)。

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


允许您仍然使用 mixin 的最简单的解决方案是放弃 v-model 并使用 :value@input 绑定。示例:

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

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

这样所有修改都在突变中完成。最后一点,如果你觉得:value@input 很冗长,你可以创建一个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。演示(与小提琴相同的代码):

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>

【讨论】:

我知道这两种方式都不是标准做法,我应该使用值和输入绑定,但试图避免它过于冗长,因为商店中有许多不同的数据类型,包括日期。我认为自定义指令肯定会有所帮助,因此感谢您为我指出正确的方向。我最初正在考虑执行类似 update Prof function you have here 之类的操作,它将键作为参数传递,但是有许多对象的深度超过一层,因此它需要许多单独的函数。虽然我猜这是流动的。再次感谢! 是的!如果有任何问题,请告诉我(如果需要,您可以随时打开一个新问题并在此处联系我)! 啊,关于这个,我看到你是新来的,所以,如果你认为这个答案很有效,你可以考虑接受它:meta.stackexchange.com/a/5235/219205 啊[2],我看到你刚刚提到了第一个替代方案。但也要检查第二个。如果您有纯数据(以便克隆功能足够好),它可能是整体上最好/更短的解决方案!

以上是关于未使用 Vuex 中的计算属性注册突变的主要内容,如果未能解决你的问题,请参考以下文章

Vuex + VueJS:未定义将计算属性传递给孩子

在命名空间模块中调用使用方括号(计算机属性名称)定义的 Vuex 操作

如何在 Vuetify 组件中使用 Vue v-model 绑定以及计算属性和 Vuex?

Vue / Vuex:双向计算属性 - 未定义不是重新加载的对象

vue的计算属性 是怎么关联某个数据

Vuex:使用存储变量的计算属性中的方法