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

新手上路,请多包涵

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

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

父组件通过 props 传递视频的数据。我也有这一套可用于多个视频,它适用于单个或多个视频。

 <div v-for="video in videos" :key="video.id">
  <video-player :videoName="video.videoName" :videoURL="video.videoURL" :thumbnail="video.thumbnail"></video-player>
</div>

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

 getFreeVideo: [
  {
    videoName: "the_video_name",
    videoURL:  "https://demo-video-url.mp4",
    thumbnail: "https://s3.amazonaws.com/../demo-video-poster.jpg"
  }
]

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

  data () {
   return {
     videos: []
   }
 }

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

     this.videos = this.getFreeVideo

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

  this.$store.dispatch('getFreeVideo', 'the_video_name')

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

我正在使用 mapState import { mapState } from 'vuex 来观察状态变化。

  computed: {
  ...mapState(['getFreeVideo'])
}

我不明白为什么 this.videos 没有更新。在这里,我对预期行为的假设是 videos[] 从状态更改和组件的重新渲染中更新。

如下所示,状态已更新,计算属性中的 videoUpdate() 也包含新数据:

VuexVuex ..但是, videos[] 永远不会更新,因此视频组件永远不会获得道具新道具等。

几点注意事项:

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

TL;博士

状态更改后,我基本上无法让子组件重新渲染。虽然我可以使用不同的结构将数据放入 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
}

原文由 Jordan 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 763
2 个回答

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


现在,关于这个问题, _根据您发布的代码_,考虑模板:

 <div v-for="video in videos" :key="video.id">

它从以下位置选择 videos

  data () {
   return {
     videos: freeVideo
   }
 }

尽管它从 freeVideo 初始化,但在您的代码中没有任何地方显示 videos 的更新

解决方案:

您已经在 getFreeVideo 中映射了状态:

 computed: {
  ...mapState(['getFreeVideo'])
}

用它:

 <div v-for="video in getFreeVideo" :key="video.id">


更新:

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

>      this.videos = this.getFreeVideo
>
> ```

这不足以让 `this.videos` 更新为 `this.getFreeVideo` 是什么。每当设置为 `this.getFreeVideo` 时,它只会更改 `this.getFreeVideo` ,而不是 `this.videos` 。

如果您想在 `this.getFreeVideo` 更改时自动更新 `this.videos` ,请创建一个观察者:

watch: { getFreeVideo() { this.videos = this.getFreeVideo } }


然后在 \-\-\- 中继续使用 `videos` `v-for` :


# Vue 的反应性

> 以下所有解释仅适用于 Vue2。 Vue3 没有任何这些警告。

如果你的状态没有在视图中更新,也许你没有在最好的状态下探索 Vue:

> 要让 Vue _**自动**_ 响应值的变化,对象必须 **最初在 `data` 中声明**。或者,如果没有,则必须 **使用 `Vue.set()` 添加** 它们。

请参阅下面演示中的评论。或者 [在此处的 **JSFiddle** 中打开相同的演示](https://jsfiddle.net/acdcjunior/ynm9nLmo/)。

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.';
}

} })


/* 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 }


person.name: {{ person.name }}
person.nickname: {{ person.nickname }}
person.address: {{ person.address }}





For more info, read the comments in the code. Or check the docs on Reactivity (link below).
“`

要掌握 Vue 的这一部分,请查看 Official Docs on Reactivity - Change Detection Caveats 。这是必须阅读的!

原文由 acdcjunior 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

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

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

原文由 Jordan 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题