Vue.js:数据未随状态更改而更新,因此不会发生重新渲染
Vue.js: Data is not updating with state change so the re-render does not happen
我有一个可重复使用的组件,它是一个 video.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
里面的data里设置的(后来设置到getFreeVideo)
data () {
return {
videos: []
}
}
我将 data() 中的 videos
设置为 created() 生命周期内商店中的 getFreeVideo:
this.videos = this.getFreeVideo
..并检查用户是否有个人视频并更新 created() 生命周期中的状态。
this.$store.dispatch('getFreeVideo', 'the_video_name')
这成功地使用 axios 和 returns 我们的视频数据发出请求。
我正在使用 mapState import { mapState } from 'vuex
来观察状态变化。
computed: {
...mapState(['getFreeVideo'])
}
我不明白为什么 this.videos
没有更新。
在这里,我对预期行为的假设是 videos[]
从状态更改和组件的重新渲染中更新。
如下所示,状态已更新,计算属性中的 videoUpdate()
也有新数据:
..但是,videos[]
永远不会更新,因此视频组件永远不会获得道具新道具等..
一些注意事项:
- 已经尝试过,用
v-if
隐藏子组件(状态改变后显示)
- 已尝试
setTimeout
进行测试,但数据会通过,然后 videoJS 播放器无法正确实例化 (必须有初始数据)
- 尝试使用本地方法/不使用 Vuex 状态
- 控制台显示错误
TypeError: Cannot read property '_withTask' of undefined
但即使演示视频正确加载也会发生这种情况,所以这看起来无关紧要,而且我在这里找不到任何显示为未定义的内容。
TL;DR
状态改变后,我基本上无法让子组件重新渲染。
尽管我可以使用不同的结构将数据放入 videos[]
中,但它仍然不会重新呈现。
为什么数据没有通过,并且从未发生重新渲染?
请不要 post 仅包含指向 'understanding reactivity' 的链接或没有任何解释的内容的答案。
附加到@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
}
如果计算的 属性 没有被引用(例如 "used")在你的模板代码中的某处,vue 将跳过它的反应性。
首先,您构建商店和状态属性的方式有点令人困惑。
我会:
1) 在商店状态"videos" 属性
2) 初始化为空数组
3) 在应用程序启动时使用 "load" 默认值正确填充它,并使用将 "default" 视频推送到它的突变
4) 在 videos
的名称下添加组件 mapGetter
5) 每当您加载 "updates" 可能视频的组件时,然后调度操作并调用适当的突变来替换存储 "videos" 属性
注意:如果组件可以有不同的 "default" 视频,那么您可能希望商店中的 videos
属性 初始化为 false
.然后,这允许您计算 属性,它使用 getter 作为商店中的 videos
属性,如果它是 false
我的意思是,对于第一种情况
// 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
}
}
}
个人:给他们更好的名字-_-第一个选项最清楚
注意:在这个答案的底部,请参阅我关于 update/reactivity 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">
更新:
I'm setting videos
in data() to getFreeVideo in the store within the
created() lifecycle:
this.videos = this.getFreeVideo
这不足以使 this.videos
与 this.getFreeVideo
保持同步。每当将某些内容设置为 this.getFreeVideo
时,它只会更改 this.getFreeVideo
,而不会更改 this.videos
。
如果您想在 this.getFreeVideo
发生变化时自动更新 this.videos
,请创建一个观察者:
watch: {
getFreeVideo() {
this.videos = this.getFreeVideo
}
}
然后在 v-for
:
中继续使用 videos
<div v-for="video in videos" :key="video.id">
Vue 的反应性
如果您的状态未在视图中更新,则可能您没有充分探索 Vue:
To have Vue automatically react to value changes, the objects must be initially declared in data
. Or, if not, they must be added using 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。必读!
原来整个问题都出在 video.js 上。我有点想删除这个问题,但我不希望任何帮助过的人失去任何分数。
这里的解决方案和输入确实有助于重新思考我对观察者的使用或我是如何尝试这样做的。我现在只使用中央商店,因为它工作正常,但稍后需要重构。
我不得不暂时放弃使用 video.js 作为播放器,而普通的 html5 视频播放器可以正常工作。
我有一个可重复使用的组件,它是一个 video.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
里面的data里设置的(后来设置到getFreeVideo)
data () {
return {
videos: []
}
}
我将 data() 中的 videos
设置为 created() 生命周期内商店中的 getFreeVideo:
this.videos = this.getFreeVideo
..并检查用户是否有个人视频并更新 created() 生命周期中的状态。
this.$store.dispatch('getFreeVideo', 'the_video_name')
这成功地使用 axios 和 returns 我们的视频数据发出请求。
我正在使用 mapState import { mapState } from 'vuex
来观察状态变化。
computed: {
...mapState(['getFreeVideo'])
}
我不明白为什么 this.videos
没有更新。
在这里,我对预期行为的假设是 videos[]
从状态更改和组件的重新渲染中更新。
如下所示,状态已更新,计算属性中的 videoUpdate()
也有新数据:
videos[]
永远不会更新,因此视频组件永远不会获得道具新道具等..
一些注意事项:
- 已经尝试过,用
v-if
隐藏子组件(状态改变后显示) - 已尝试
setTimeout
进行测试,但数据会通过,然后 videoJS 播放器无法正确实例化 (必须有初始数据) - 尝试使用本地方法/不使用 Vuex 状态
- 控制台显示错误
TypeError: Cannot read property '_withTask' of undefined
但即使演示视频正确加载也会发生这种情况,所以这看起来无关紧要,而且我在这里找不到任何显示为未定义的内容。
TL;DR
状态改变后,我基本上无法让子组件重新渲染。
尽管我可以使用不同的结构将数据放入 videos[]
中,但它仍然不会重新呈现。
为什么数据没有通过,并且从未发生重新渲染?
请不要 post 仅包含指向 'understanding reactivity' 的链接或没有任何解释的内容的答案。
附加到@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
}
如果计算的 属性 没有被引用(例如 "used")在你的模板代码中的某处,vue 将跳过它的反应性。
首先,您构建商店和状态属性的方式有点令人困惑。
我会:
1) 在商店状态"videos" 属性
2) 初始化为空数组
3) 在应用程序启动时使用 "load" 默认值正确填充它,并使用将 "default" 视频推送到它的突变
4) 在 videos
mapGetter
5) 每当您加载 "updates" 可能视频的组件时,然后调度操作并调用适当的突变来替换存储 "videos" 属性
注意:如果组件可以有不同的 "default" 视频,那么您可能希望商店中的 videos
属性 初始化为 false
.然后,这允许您计算 属性,它使用 getter 作为商店中的 videos
属性,如果它是 false
我的意思是,对于第一种情况
// 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
}
}
}
个人:给他们更好的名字-_-第一个选项最清楚
注意:在这个答案的底部,请参阅我关于 update/reactivity 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">
更新:
I'm setting
videos
in data() to getFreeVideo in the store within the created() lifecycle:this.videos = this.getFreeVideo
这不足以使 this.videos
与 this.getFreeVideo
保持同步。每当将某些内容设置为 this.getFreeVideo
时,它只会更改 this.getFreeVideo
,而不会更改 this.videos
。
如果您想在 this.getFreeVideo
发生变化时自动更新 this.videos
,请创建一个观察者:
watch: {
getFreeVideo() {
this.videos = this.getFreeVideo
}
}
然后在 v-for
:
videos
<div v-for="video in videos" :key="video.id">
Vue 的反应性
如果您的状态未在视图中更新,则可能您没有充分探索 Vue:
To have Vue automatically react to value changes, the objects must be initially declared in
data
. Or, if not, they must be added usingVue.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。必读!
原来整个问题都出在 video.js 上。我有点想删除这个问题,但我不希望任何帮助过的人失去任何分数。
这里的解决方案和输入确实有助于重新思考我对观察者的使用或我是如何尝试这样做的。我现在只使用中央商店,因为它工作正常,但稍后需要重构。
我不得不暂时放弃使用 video.js 作为播放器,而普通的 html5 视频播放器可以正常工作。