【问题标题】:Vue JS computed property is undefined (async axios call in vuex)Vue JS 计算属性未定义(vuex 中的异步 axios 调用)
【发布时间】:2020-08-23 20:35:05
【问题描述】:

所以问题在于,除了一个特定的属性之外,所有在存储中有 axios 调用的计算属性都运行良好。 GetSong.owner 未定义,但问题是数据在网页上正确显示。该错误仅在刷新时出现,所以我猜它与在存储中的 axios 调用完成之前加载的 dom 有关。有任何想法吗?这是代码:

<div>
   <div class="song">
       <div class="song_container">
           <div class="song_info">
               <h1 v-if="getSong" class="song_info-author">{{ getSong.owner.profile[0].name ? getSong.owner.profile[0].name : '' }}</h1>
               <h1 class="song_info-title">{{  getSong.title }} </h1>
               <h1 class="song_info-genre">{{  getSong.genre }} </h1>
           </div>
           <div class="song_playback">
               <app-player></app-player>
           </div>
        <img v-bind:class="{'liked': getLiked}" @click.prevent="likeUnlikeSong" 
       class="song_like" src="../assets/icons/favourite.svg" alt="" srcset="">
       </div>
       <app-comment></app-comment>
   </div>
</div>
</template>

<script>
import Comment from "../components/Comment.vue";
import Player from "../components/Player.vue";
export default {
    components : {
       appComment : Comment,
       appPlayer : Player
   },
   computed: {
       getSong(){
           return this.$store.getters.getSong
       },
       getLiked() {
           return this.$store.getters.getLiked
       },
       getProfile() {
           return this.$store.getters.getProfile
       },
       getImage() {
       return this.$store.getters.getImage
       }
   },
   methods : {
         likeUnlikeSong (){
           if(!this.getLiked){
               this.$store.dispatch('LIKE_SONG',this.$route.params.id)
           } else {
               this.$store.dispatch('UNLIKE_SONG',this.$route.params.id)
           }
       },
   },
   created() {
        this.$store.dispatch('GET_SONG',this.$route.params.id)
        this.$store.dispatch('CHECK_LIKE',this.$route.params.id)

   }

}
</script>

<style lang="scss" scoped>
@import '../scss/components/song';

</style> 

商店:


const song = {
    state : {
        song : {},
        songs: [],
        my_songs: [],
        liked: false
    },
    getters : {
        getSong : (state) => {
            return state.song
        },
        getAllSongs: (state) => {
            return state.songs
        },
        getLiked : (state) => {
            return state.liked
        },
        getMySongs : (state) => {
            return state.my_songs
        }
    },
    mutations : {
        setSongOne : (state,song) => {
            state.song = song
        },
        removeSongOne : (state) => {
            state.song = null
        },
        setAllSongs : (state,songsArray) => {
           state.songs = songsArray
        },
        emptyAllSongs: (state) => {
            state.songs = []
        },
        setLike: (state,boolean) => {
            state.liked = boolean
        },
        setMySongs : (state,songs) => {
            songs.forEach((song) => {
                state.my_songs.push(song)
            })
        },
        reset: (state) => {
            state.song = {},
            state.songs = [],
            state.my_songs = [],
            state.liked =  false
        }
    },
    actions : {
        UPLOAD : ({commit}, payload) => {
            return new Promise((resolve,reject) => {
                Axios.post('/song',payload)
                .then((success) => {
                   resolve()
                }).catch((error) => {
                    reject(error)
                })

            })

        },
        GET_SONG: ({commit}, payload) => {
                Axios.get(`/song/${payload}`).then(({data,status}) => {
                    if(status===200){
                        commit('setSongOne',data)
                    } 
                }).catch((error) => {
                })

        },
        GET_MY_SONGS : ({commit,getters}) => {
            if(getters.getMySongs.length == 0){
            return new Promise((resolve,reject) => {
                Axios.get('/song/me').then(({data,status}) => {
                    if(status=== 200) {
                        commit('setMySongs',data)
                        resolve()
                    }
                }).catch((error) => {
                    reject(error)
                })
            })}
        },
        GET_ALL_SONGS: ({commit}) => {
            let songs = []
            return new Promise((resolve,reject) => {
                Axios.get('/song').then(({data,status}) => {
                    if(status===200) {
                        songs.push(data)
                        commit('setAllSongs',songs)
                        resolve()
                    }
                }).catch((error) => {
                    reject(error)
                })
            })

        },
        EMPTY_ALL_SONGS: ({commit}) => {
            commit('emptyAllSongs')
        },
        LIKE_SONG : ({commit},payload) => {
             return new Promise((resolve,reject) => {
                Axios.post(`/song/${payload}/like`).then(({status}) => {
                    if(status===200) {
                        commit('setLike',true)
                        resolve()
                    } 
                }).catch((error) => {
                    reject(error)
                })
             })          
        },
        UNLIKE_SONG : ({commit},payload) => {
            return new Promise((resolve,reject) => {
               Axios.post(`/song/${payload}/unlike`).then(({status}) => {
                   if(status===200) {
                       commit('setLike',false)
                       resolve()
                   }
               }).catch((error) => {
                reject(error)
            })
            })          
       },
        CHECK_LIKE: ({commit},payload) => {
            return new Promise((resolve,reject) => {
                Axios.get(`/song/${payload}/check_like`).then(({status,data}) => {
                    if(status===200) {
                        commit('setLike',data.liked)
                        resolve()
                    } 
                }).catch((error) => {
                    reject(error)
                })
            })
        }
    }
}

export default song

【问题讨论】:

    标签: javascript vue.js axios


    【解决方案1】:

    正如你所说,它是在数据加载之前渲染的。

    问题在于您将商店状态默认为song: {},因此v-if="getSong" 始终为真。

    一个快速的解决方法是 v-if="getSong.owner"。就我个人而言,我不会那样修复它,我会将其更改为 song: null 并在模板更高的位置处理丢失的歌曲。最终,您需要决定在加载数据时要渲染什么。在本地,这可能需要几毫秒,但您应该考虑在生产中可能需要多长时间以及您希望用户看到什么。

    在不相关的说明中,您不应该将 axios 调用包装在 Promise 中。 Axios 无论如何都会返回 Promise,所以你应该返回它们。例如:

    UPLOAD ({commit}, payload) {
      return Axios.post('/song', payload)
    })
    

    更新:

    created 挂钩将在渲染发生之前运行,但您正在发出异步请求,并且它不会等待这些请求完成后再继续渲染。

    在它首次呈现时,商店状态仍将包含您的默认值。在大多数情况下,您的模板处理这些默认值的方式不会导致错误,getSong.owner.profile 部分除外。 getSong 将是一个空对象,因此 getSong.owner 将是 undefined,导致尝试访问其 profile 属性时出错。

    请注意,仅仅因为其他值没有导致错误并不一定意味着它们“正常”工作。在加载实际值时,用户将看到部分填充的 UI。这在完成的应用程序中不太可能是需要的。

    加载实际值时,它将再次渲染,这次没有错误。 setTimeout 是不必要的,您只需要编写模板来处理丢失的数据。

    如前所述,一种方法是设置song: null 并将v-if="getSong" 进一步向上移动模板,这样就不会尝试使用getSong

    【讨论】:

    • 是的,我修复了 Axios 承诺的问题。我之前尝试过 getSong.owner 但它没有修复。
    • 编辑:我也尝试在商店中将歌曲设置为空,但它仍然不起作用。我一直认为 created 在加载 dom 之前运行。此外,这真的很奇怪,这总是发生在这个特定的 axios 调用上,而不是其他调用上。您认为在这种情况下设置超时会是一个不好的解决方案吗?
    • @mrgatos 我已经更新了我的答案,提供了与您上次评论相关的更多信息。
    • 谢谢你。问题解决了。你让我明白到底发生了什么。我还在div的上半部分使用了v-if getSong
    【解决方案2】:

    好的,我解决了这个问题。对于所有遇到同样问题并使用 express 和 mongoose 工作的人。似乎是猫鼬引起了这个问题,因为我在 1 次通话中有 2 次填充。我更改了填充发生的顺序,并且错误以某种方式消失了。

    【讨论】:

      猜你喜欢
      • 2018-05-12
      • 1970-01-01
      • 2018-05-03
      • 2020-03-28
      • 2020-10-30
      • 2020-08-08
      • 2021-03-17
      • 2019-10-19
      相关资源
      最近更新 更多