未使用 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
添加到您的商店,您会看到两种情况下都会抛出错误。
在这两种替代方案中,prof
的 name
、email
和 office
属性都被直接修改(这违反了 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 操作
如何在 Vuetify 组件中使用 Vue v-model 绑定以及计算属性和 Vuex?