代码之家  ›  专栏  ›  技术社区  ›  Jordan

Vue。js:数据不会随状态更改而更新,因此不会发生重新渲染

  •  36
  • Jordan  · 技术社区  · 7 年前

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

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

    父组件通过道具传递视频数据。我也有这套可以用于多个视频,它可以很好地用于一个或多个视频。

    <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: []
       }
     }
    

    我正在设置 视频 在创建的()生命周期内,在data()中获取存储中的免费视频:

        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() 在计算属性中也有新数据:

    vuex Vuex 。。但是 视频[] 从未更新,因此视频组件从未获得道具新道具等。。

    几点注意事项:

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

    TL;DR公司

    我基本上无法让子组件在状态更改后重新渲染。 虽然我可以把数据 视频[] 使用不同的结构,它仍然不会重新渲染。

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

    请不要发布只包含“了解反应性”链接或其他没有任何解释的答案。 ðŸ˜

    为@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
    }
    
    3 回复  |  直到 6 年前
        1
  •  48
  •   acdcjunior Mukul Kumar    5 年前

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


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

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

    它选择 videos 发件人:

     data () {
       return {
         videos: freeVideo
       }
     }
    

    虽然它从初始化 freeVideo ,在代码中的任何地方都不会显示 更新的 视频

    解决方案:

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

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

    使用它:

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

    更新时间:

    我正在设置 视频 在data()中获取存储中的免费视频 已创建()生命周期:

        this.videos = this.getFreeVideo
    

    这还不够 this.videos 用任何东西更新 this.getFreeVideo 是无论什么时候 这getFreeVideo 它只会改变 这getFreeVideo 这视频

    如果要自动更新 这视频 无论何时 这getFreeVideo 更改,创建观察者:

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

    然后继续使用 视频 v-for :

    <div v-for=“视频中的视频”:key=“video.id”>
    



    Vue反应性

    如果您的状态没有在视图中得到更新,则可能您没有在最佳状态下探索Vue:

    拥有Vue 自动地 对值更改作出反应时,对象必须 最初在中声明 data 。或者,如果不是,他们一定是 使用添加 Vue.set()

    请参见下面演示中的注释。或打开 same demo in a JSFiddle here

    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 }
    <script src="https://unpkg.com/vue"></script>
    
    <div id="app">
      <span>person.name: {{ person.name }}</span><br>
      <span>person.nickname: {{ person.nickname }}</span><br>
      <span>person.address: {{ person.address }}</span><br>
      <br>
      <button @click="changeName">this.person.name = 'Arantes'; (will auto update because `name` was in `data`)</button><br>
      <button @click="changeNickname">this.person.nickname = 'Pele'; (will NOT auto update because `nickname` was not in `data`)</button><br>
      <button @click="changeNicknameProperly">Vue.set(this.person, 'address', '99th st.'); (WILL auto update even though `address` was not in `data`)</button>
      <br>
      <br>
      For more info, read the comments in the code. Or check the docs on <b>Reactivity</b> (link below).
    </div>

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

        2
  •  3
  •   Jordan    7 年前

    原来整个问题都是关于视频的。js。我有点想删除这个问题,但我不希望任何帮助过我的人失去任何分数。

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

    我不得不放弃使用视频。js目前只是一个播放器,而一个普通的html5视频播放器工作起来没有问题。

        3
  •  2
  •   ElectRocnic    6 年前

    如果模板代码中的某个地方未引用计算属性(例如“使用”),vue将跳过该属性的反应性。

    首先,您构建存储和状态属性的方式有点混乱。

    我会:

    1) 在商店状态下拥有“视频”属性

    2) 将其初始化为空数组

    3) 在应用程序启动时,使用“加载”默认值正确填充它,并使用将“默认”视频推送到其中的变体

    4) 拥有组件 mapGetter 以其名义 videos

    5) 无论何时加载“更新”可能视频的组件,都会调度该操作并调用相应的变体来替换存储“视频”属性

    注意:如果组件可以有不同的“默认”视频,那么您可能需要 视频 商店中初始化为的属性 false 。然后,这允许您拥有一个计算属性,该属性将getter用于 视频 商店中的财产,如果是 错误

    我的意思是,对于第一种情况

    // store
    state: {
      videos: []
    }
    
    getters: {
      videos(state) { return state.videos } 
    }
    
    //components
    
    ...
    computed: {
      videos() {
        this.$store.getters.videos
      }
    }
    
    For the second case
    
    
    // store
    state: {
      videos: false
    }
    
    getters: { personal_videos(state) { return state.videos } }
    
    //components
    data() { return { default: default_videos } },
    computed: {
      ...mapGetters([ 'personal_videos' ]),
      videos() {
        if (this.personal_videos) {
          return this.personal_videos
        } else {
          return this.default
        } 
      }
    

    }

    个人:给他们起个更好的名字,第一个选择是最清楚的