Vue js似乎将嵌套对象作为字符串返回?

时间:2018-03-15 03:46:47

标签: javascript object vue.js

我有一个对象

   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),它就会正确返回。并且是一个完整的对象。为什么当我在模板中访问它时,它是否对任何嵌套对象进行字符串化?

1 个答案:

答案 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.testundefined,因此它会尝试访问undefined.test,这会引发错误:

&#13;
&#13;
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;
&#13;
&#13;

没有.test,没有错误:

&#13;
&#13;
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;
&#13;
&#13;

解决方法.testv-if

&#13;
&#13;
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;
&#13;
&#13;

解决方法.test但初始化user包含details属性:

&#13;
&#13;
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;
&#13;
&#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(请参阅上面的最后两个演示)。