我在Vuex getter中发现了JSON的一些奇怪行为:看来这是导致按引用传递类型的问题的原因。对于上下文-我正在开发一个音乐应用程序,它将具有多个“场景”,每个场景都包含“曲目”的集合(类似于Ableton Live)。
这是我的吸气剂:
newTrack: state => {
let newTrack = JSON.parse(JSON.stringify(state.newTrackDefaults))
return newTrack
},
Run Code Online (Sandbox Code Playgroud)
这是它所指的对象:
newTrackDefaults: {
tune: [],
// and other properties
},
Run Code Online (Sandbox Code Playgroud)
然后由一个动作调用它:
setUpNewScene: context => {
let newScene = JSON.parse(JSON.stringify(context.state.newSceneDefaults))
let newTrack = context.getters.newTrack
console.log(newTrack) // this reveals that the problem is from the getter
newScene.tracks.push(newTrack)
context.commit('addNewScene', newScene)
}
Run Code Online (Sandbox Code Playgroud)
因此,此代码的问题是,当我在第一个场景的轨道上添加项目(音高参考),然后添加新场景时,新场景会自动接收与第一个场景相同的轨道。这反映在Vuex状态中(根据DevTool),而不仅仅是渲染。而且,当用户更新第一场景上的轨道时,新场景上的轨道相应地改变。因此,本能地感觉就像是按引用传递类型的错误。
通过各种console.log实验,我发现吸气剂正在返回“填充”轨迹。通过跳过getter并将操作编写为:
setUpNewScene: context => {
let newScene = JSON.parse(JSON.stringify(context.state.newSceneDefaults))
let newTrack = JSON.parse(JSON.stringify(context.state.newTrackDefaults))
console.log(newTrack) // this works fine
newScene.tracks.push(newTrack)
context.commit('addNewScene', newScene)
}
Run Code Online (Sandbox Code Playgroud)
...尽管我有个解决办法,但我对最初的行为感到困惑。吸气剂会干扰JSON功能吗?还是可能导致这种情况的其他原因?
Vuex getter被缓存。
https://vuex.vuejs.org/en/getters.html
像计算的属性一样,getter的结果将基于其依赖项进行缓存,并且仅在其某些依赖项发生更改时才重新评估。
如果其依存关系不变,则获取器将返回其最后返回的值。大概你不是在变异newTrackDefaults。这意味着吸气剂newTrack将永远不会更新。这意味着context.getters.newTrack每次都返回相同的对象。因此,您认为自己正在创建的所有“新”轨道实际上都是相同的轨道。
当处理默认值时,我喜欢创建一个返回默认值的函数。然后,我可以随时随地调用该函数,而无需进行任何时髦的缓存业务。
function newTrackDefaults() {
return {
tune:[],
// and other properties
}
}
setUpNewScene: context => {
let newScene = JSON.parse(JSON.stringify(context.state.newSceneDefaults))
let newTrack = newTrackDefaults()
newScene.tracks.push(newTrack)
context.commit('addNewScene', newScene)
}
Run Code Online (Sandbox Code Playgroud)
有趣的事实:吸气剂可以返回函数,因此您也可以像这样修复它。
newTrack: state => () => {
let newTrack = JSON.parse(JSON.stringify(state.newTrackDefaults))
return newTrack
},
Run Code Online (Sandbox Code Playgroud)
现在您需要调用getter,因为它现在是一个函数。
let newTrack = context.getters.newTrack()
Run Code Online (Sandbox Code Playgroud)