我有一个对象
var user = {
name:"test",
number:"9666-0503",
details:{
test:"cannot_access_this",
second_field:"nope_no_go"
}
}
我在Vue JS中调用一个动作
[TYPES.FETCH_USER]({ commit }) {
api.get('user').then(({ data }) => {
commit(TYPES.STORE_USER, data)
console.log(data)
// This returns perfectly fine and valid. If I do
//console.log(data.details.test), I get the correect value
}, (error) => {
console.log(error)
})
},
然后突变
[TYPES.STORE_USER](state, data) {
state.user = data
localStorage.set("user", state.user)
},
在我的getters文件中
getUser: state => {
return state.user
}
在我的组件中我正在做
computed: {
...mapGetters(["getUser"]),
},
mounted(){
this.getData()
},
methods: {
getData() {
this.$store.dispatch(TYPES.FETCH_USER);
}
}
在模板中
<h1>{{getUser.name}}</h1><!-- this works -->
<h2>{{getUser.number}}</h2><!-- this works -->
<h3>{{getUser.details.test}}</h3> <!-- THIS FAILS!!-->
所以getUser.details.test失败了。但是......如果我只是做
<h3>{{getUser.details}}</h3>
然后打印出看似详细对象的字符串版本??!如下所示....
<h3>{"test":"cannot_access_this","second_field":"nope_no_go"}</h3>
这里发生了什么!??
最奇怪的是,如果我在任何时候只是console.log(state.user),它就会正确返回。并且是一个完整的对象。为什么当我在模板中访问它时,它是否对任何嵌套对象进行字符串化?
答案 0 :(得分:1)
Mutations must be synchronous.在突变中使用异步代码会产生各种无法追踪的奇怪行为。
正如文档建议的那样,您应该将非同步代码移动到操作,例如:
mutations: {
[TYPES.FETCH_USER](state, data) {
state.user = data
}
//...
},
actions: {
fetchUsersActionName({ commit }) {
return api.get('user').then(({ data }) => {
commit(TYPES.FETCH_USER, data)
console.log(data)
}, (error) => {
console.log(error)
});
}
}
并将其与.dispatch()
一起使用,而不是.commit()
,例如:
this.$store.dispatch('fetchUsersActionName');
检查以下演示,了解您收到错误的原因:
<h1>{{getUser.name}}</h1><!-- this works --> <h2>{{getUser.number}}</h2><!-- this works --> <h3>{{getUser.details.test}}</h3> <!-- THIS FAILS!!-->
第一个演示会抛出一个错误,因为最初getUser.details.test
是undefined
,因此它会尝试访问undefined.test
,这会引发错误:
var globalUser = {
name:"test",
number:"9666-0503",
details:{
test:"cannot_access_this",
second_field:"nope_no_go"
}
};
var TYPES = {FETCH_USER: 'TYPESFETCHUSER'};
const store = new Vuex.Store({
strict: true,
state: {user: {}},
mutations: {
[TYPES.FETCH_USER](state, data) {
state.user = data
}
},
actions: {
fetchUsersActionName({ commit }) {
setTimeout(() => commit(TYPES.FETCH_USER, globalUser, 2000)); // simmulate asynchronous code
}
},
getters: {
getUser: state => state.user
}
});
new Vue({
store: store,
el: '#app',
computed: {
...Vuex.mapGetters(['getUser']),
},
mounted() {
this.getData()
},
methods: {
getData() { this.$store.dispatch('fetchUsersActionName'); }
}
})
&#13;
span {font-family: monospace; font-weight: bold; color: red } h1,h2,h3 {font-size: medium; display: inline-block}
&#13;
<script src="https://unpkg.com/vue@2.5.16/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex"></script>
<span>This throws an error, because initially .details is undefined.</span>
<div id="app">
<h1>{{getUser.name}}</h1> |
<h2>{{getUser.number}}</h2> |
<h3>{{getUser.details.test}}</h3>
</div>
&#13;
没有.test
,没有错误:
var globalUser = {
name:"test",
number:"9666-0503",
details:{
test:"cannot_access_this",
second_field:"nope_no_go"
}
};
var TYPES = {FETCH_USER: 'TYPESFETCHUSER'};
const store = new Vuex.Store({
strict: true,
state: {user: {}},
mutations: {
[TYPES.FETCH_USER](state, data) {
state.user = data
}
},
actions: {
fetchUsersActionName({ commit }) {
setTimeout(() => commit(TYPES.FETCH_USER, globalUser, 2000)); // simmulate asynchronous code
}
},
getters: {
getUser: state => state.user
}
});
new Vue({
store: store,
el: '#app',
computed: {
...Vuex.mapGetters(['getUser']),
},
mounted() {
this.getData()
},
methods: {
getData() { this.$store.dispatch('fetchUsersActionName'); }
}
})
&#13;
span {font-family: monospace; font-weight: bold; color: red } h1,h2,h3 {font-size: medium; display: inline-block}
&#13;
<script src="https://unpkg.com/vue@2.5.16/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex"></script>
<span>This throws no error, and prints the whole details object.</span>
<div id="app">
<h1>{{getUser.name}}</h1> |
<h2>{{getUser.number}}</h2> |
<h3>{{getUser.details}}</h3>
</div>
&#13;
解决方法:.test
但v-if
:
var globalUser = {
name:"test",
number:"9666-0503",
details:{
test:"cannot_access_this",
second_field:"nope_no_go"
}
};
var TYPES = {FETCH_USER: 'TYPESFETCHUSER'};
const store = new Vuex.Store({
strict: true,
state: {user: {}},
mutations: {
[TYPES.FETCH_USER](state, data) {
state.user = data
}
},
actions: {
fetchUsersActionName({ commit }) {
setTimeout(() => commit(TYPES.FETCH_USER, globalUser, 2000)); // simmulate asynchronous code
}
},
getters: {
getUser: state => state.user
}
});
new Vue({
store: store,
el: '#app',
computed: {
...Vuex.mapGetters(['getUser']),
},
mounted() {
this.getData()
},
methods: {
getData() { this.$store.dispatch('fetchUsersActionName'); }
}
})
&#13;
span {font-family: monospace; font-weight: bold; color: red } h1,h2,h3 {font-size: medium; display: inline-block}
&#13;
<script src="https://unpkg.com/vue@2.5.16/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex"></script>
<span>This throws no error, because the v-if controls the displaying to only when .test is available.</span>
<div id="app">
<h1>{{getUser.name}}</h1> |
<h2>{{getUser.number}}</h2> |
<h3 v-if="getUser.details">{{getUser.details.test}}</h3>
</div>
&#13;
解决方法:.test
但初始化user
包含details
属性:
var globalUser = {
name:"test",
number:"9666-0503",
details:{
test:"cannot_access_this",
second_field:"nope_no_go"
}
};
var TYPES = {FETCH_USER: 'TYPESFETCHUSER'};
const store = new Vuex.Store({
strict: true,
state: {user: {details: {}}}, // <============ THIS IS THE IMPORTANT, notice the {details: {}}
mutations: {
[TYPES.FETCH_USER](state, data) {
state.user = data
}
},
actions: {
fetchUsersActionName({ commit }) {
setTimeout(() => commit(TYPES.FETCH_USER, globalUser, 2000)); // simmulate asynchronous code
}
},
getters: {
getUser: state => state.user
}
});
new Vue({
store: store,
el: '#app',
computed: {
...Vuex.mapGetters(['getUser']),
},
mounted() {
this.getData()
},
methods: {
getData() { this.$store.dispatch('fetchUsersActionName'); }
}
})
&#13;
span {font-family: monospace; font-weight: bold; color: red } h1,h2,h3 {font-size: medium; display: inline-block}
&#13;
<script src="https://unpkg.com/vue@2.5.16/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex"></script>
<span>This throws no error, because the user initial value contains a "details" field already.</span>
<div id="app">
<h1>{{getUser.name}}</h1> |
<h2>{{getUser.number}}</h2> |
<h3>{{getUser.details.test}}</h3>
</div>
&#13;
<强>为什么吗
这是因为更新user
的代码是异步的。
第一次尝试显示{{ getUser.name }}
时,getUser.name
值为undefined
,因此只显示(因为Vue在{{1}时没有显示任何内容})。毫秒之后,{{ undefined }}
会更新,然后开始显示getUser.name
。它非常快,这就是为什么你会感受到它从未name
的感觉。
undefined
和{{ getUser.number }}
也是如此。
另一方面,当它试图显示{{ getUser.details }}
时会产生错误,因为,{{ getUser.details.test }}
getUser.details
起初是undefined
所以当它尝试访问{{1}时它实际上与getUser.details.test
相同,这就是它抛出undefined.test
的原因。
对于变通方法,请使用TypeError: Cannot read property 'test' of undefined
或使用非v-if
user
属性初始化undefined
(请参阅上面的最后两个演示)。