Vue.js:数据不会随着状态变化而更新,因此不会发生重新渲染

Jor*_*dan 10 video.js vue.js vuejs2

我有一个可重用的组件,它是video.js视频播放器。当在初始DOM加载中传递数据时,此组件可以正常工作。

我需要弄清楚为什么在Vuex中更新状态后为什么我的组件没有重新渲染。

父组件通过prop传递视频数据。我也可以将此设置用于多个视频,并且单个或多个视频都可以正常使用。

<div v-for="video in videos" :key="video.id">
  <video-player :videoName="video.videoName" :videoURL="video.videoURL" :thumbnail="video.thumbnail"></video-player>
</div>
Run Code Online (Sandbox Code Playgroud)

我正在为Vuex存储中的所有用户将初始状态设置为通用视频。

getFreeVideo: [
  {
    videoName: "the_video_name",
    videoURL:  "https://demo-video-url.mp4",
    thumbnail: "https://s3.amazonaws.com/../demo-video-poster.jpg"
  }
]
Run Code Online (Sandbox Code Playgroud)

这是在数据中设置的videos (后来设置为getFreeVideo)

 data () {
   return {
     videos: []
   }
 }
Run Code Online (Sandbox Code Playgroud)

我正在将videosdata()设置为在created()生命周期内的商店中的getFreeVideo:

    this.videos = this.getFreeVideo
Run Code Online (Sandbox Code Playgroud)

..并检查用户是否有个人视频,并在created()生命周期中更新状态。

 this.$store.dispatch('getFreeVideo', 'the_video_name')
Run Code Online (Sandbox Code Playgroud)

这会向axios发出请求,并成功返回我们的视频数据。

我正在使用mapState import { mapState } from 'vuex来监视状态更改。

 computed: {
  ...mapState(['getFreeVideo'])
}
Run Code Online (Sandbox Code Playgroud)

我看不到为什么this.videos不被更新。在这里,我对预期行为的假设将根据videos[]状态更改和组件的重新呈现进行更新。

如您在下面看到的,状态已更新,并且videoUpdate()计算属性内的内还具有新数据:

威克斯 威克斯 ..但是,videos[]从来没有如此更新的视频分量从来没有得到新道具等道具..

一些注意事项:

  • 已经尝试过,隐藏子组件v-if (并在状态更改后显示)
  • 试图setTimeout测试事物,但数据会通过,然后videoJS播放器永远无法正确实例化(必须具有初始数据)
  • 尝试使用本地方法执行此操作/不使用Vuex状态
  • 控制台显示错误,TypeError: Cannot read property '_withTask' of undefined但是即使演示视频正确加载,也会发生这种情况,因此这似乎无关紧要,而且我在这里找不到任何显示为未定义状态的内容。

TL; DR

在状态更改后,我基本上无法重新渲染子组件。尽管我可以videos[]采用不同的结构来获取数据,但仍然永远不会重新渲染。

为什么数据无法通过,并且重新渲染从未发生?

请不要发布仅包含“了解反应性”或没有任何解释的链接的答案。

附加为@acdcjunior

//action   
getFreeVideo: (context, videoName) => {
    axios({
      method: 'post',
      url: 'https://hidden-for-posting',
      data: {
        action: 'getVideo',
        userId: '1777', // (hardcoded to test)
        videoName: videoName
      },
      headers: {
        'x-api-key': apiKey,
        'Content-Type': 'application/json'
      }
    })
    .then(response => {
      let video = [
        {
          videoName: response.data.videoName,
          videoURL: response.data.videoURLs.mp4,
          thumbnail: response.data.thumbnails['1280']
        }
      ]
      return context.commit('updateGetFreeVideo', video)
    })
    .catch(error => {
      if (error.response) {
        console.log(error.response)
      } else if (error.request) {
        console.log(error.request)
      } else {
        console.log('Error', error.message)
      }
      console.log(error.config)
    })
}

// mutation:
updateGetFreeVideo: (state, payload) => {
  return state.getFreeVideo = payload
}

// getter:
getFreeVideo: state => {
  return state.getFreeVideo
}
Run Code Online (Sandbox Code Playgroud)

acd*_*ior 11

注意:在此答案的底部,请参阅我对Vue的更新/反应性问题的一般观点。


现在,基于您发布的代码,考虑模板,关于该问题:

<div v-for="video in videos" :key="video.id">
Run Code Online (Sandbox Code Playgroud)

videos从以下选择:

 data () {
   return {
     videos: freeVideo
   }
 }
Run Code Online (Sandbox Code Playgroud)

尽管它是从初始化的 freeVideo,但在代码中的任何地方都不会显示的更新videos

解:

您已经在getFreeVideo计算出的状态中映射了状态:

computed: {
  ...mapState(['getFreeVideo'])
}
Run Code Online (Sandbox Code Playgroud)

用它:

<div v-for="video in getFreeVideo" :key="video.id">
Run Code Online (Sandbox Code Playgroud)

更新:

我正在将videosdata()设置为在created()生命周期内的商店中的getFreeVideo:

    this.videos = this.getFreeVideo
Run Code Online (Sandbox Code Playgroud)

这不足以保持this.videos最新状态this.getFreeVideo。只要设置了某些内容,this.getFreeVideo它只会更改this.getFreeVideo,不会更改this.videos

如果要this.videosthis.getFreeVideo更改时自动更新,请创建观察者:

    this.videos = this.getFreeVideo
Run Code Online (Sandbox Code Playgroud)

然后继续使用videosv-for

watch: {
  getFreeVideo() {
    this.videos = this.getFreeVideo
  }
}
Run Code Online (Sandbox Code Playgroud)



Vue的反应性

如果您的状态在视图中没有得到更新,则可能是您没有充分利用Vue:

要使Vue 自动对值更改做出反应,必须在中初始声明data对象。否则,必须使用添加Vue.set()它们。

请参阅下面的演示中的评论。或在此处的JSFiddle中打开相同的演示

<div v-for="video in videos" :key="video.id">
Run Code Online (Sandbox Code Playgroud)
new Vue({
  el: '#app',
  data: {
    person: {
      name: 'Edson'
    }
  },
  methods: {
    changeName() {
      // because name is declared in data, whenever it
      // changes, Vue automatically updates
      this.person.name = 'Arantes';
    },
    changeNickname() {
      // because nickname is NOT declared in data, when it
      // changes, Vue will NOT automatically update
      this.person.nickname = 'Pele';
      // although if anything else updates, this change will be seen
    },
    changeNicknameProperly() {
      // when some property is NOT INITIALLY declared in data, the correct way
      // to add it is using Vue.set or this.$set
      Vue.set(this.person, 'address', '123th avenue.');
      
      // subsequent changes can be done directly now and it will auto update
      this.person.address = '345th avenue.';
    }
  }
})
Run Code Online (Sandbox Code Playgroud)
/* CSS just for the demo, it is not necessary at all! */
span:nth-of-type(1),button:nth-of-type(1) { color: blue; }
span:nth-of-type(2),button:nth-of-type(2) { color: red; }
span:nth-of-type(3),button:nth-of-type(3) { color: green; }
span { font-family: monospace }
Run Code Online (Sandbox Code Playgroud)

要掌握Vue的这一部分,请查看“ 反应性 -更改检测警告”正式文档。这是一本必读的书!


Jor*_*dan 5

事实证明,整个问题出在 video.js 上。我很想删除这个问题,但我不希望任何帮助过的人失去任何分数。

这里的解决方案和输入确实有助于重新思考我对观察者的使用或我如何尝试这一点。我现在只使用中央存储,因为它工作正常,但稍后需要重构。

我现在不得不放弃使用 video.js 作为播放器,常规的 html5 视频播放器可以正常工作。

  • 我很高兴你没有删除这个问题,这对我帮助很大 (10认同)