<template>
<div class="comment-main" v-if="commentData.length!==0">
<div class="scroll-content">
<div class="list-items" v-for="(comment,cIndex) of commentData" :key="comment.commentId">
<div class="user-baseinfo">
<div class="liked">
<span
@click="setLikeComment(cIndex)"
:class="comment.commentLiked ? 'fa-thumbs-o-up liked-active' : 'fa-thumbs-o-up' "
>{{ comment.commentLikedCount !== 0 ? comment.commentLikedCount : '' }} </span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
comment: [
{
userId: 1599056,
userName: "请留步施主",
userAvatar:
"https://p4.music.126.net/Nw7PvBrD6dAef-pCS8vrDw==/109951164690544939.jpg",
commentId: 3325604169,
commentContent: "哈哈",
commentTime: 1590647018680,
commentLiked: false,
commentLikedCount: 0,
parentCommentId: 0,
},
...假设这里有20+数据
{
userId: 352679291,
userName: "预渡風华",
userAvatar:
"https://p4.music.126.net/dUskbMgLMSSJ2GYvboZLCA==/109951165021317989.jpg",
commentId: 3325315601,
commentContent:
"一个本一个听着国内流行小曲的小伙子 因为网易云音乐渐渐的走向了歧途(正道) (手动滑稽)",
commentTime: 1590627790051,
commentLiked: true,
commentLikedCount: 3,
parentCommentId: 0,
}
]
};
},
computed: {
commentData() {
return this.comment.filter(item => {
return item.parentCommentId === 0;
});
}
},
methods: {
setLikeComment(index) {
// this.commentData[index].commentLiked = true
this.$set(this.commentData, index, {
...this.commentData[index],
commentLiked: true
});
// this.$forceUpdate();
console.log(this.commentData[index])
}
}
};
</script>
<style>
.liked-active {
color: red;
}
</style>
很疑惑
this.commentData[index].commentLiked = true
这个居然直接完成了响应式 (数据和视图同时发生了改变)
而 $set 只改变了数据并没有改变视图
当添加
this.$forceUpdate()
;之后视图才发生改变
请问:
- Vue的响应式原理不是不能通过index进行改变值而达到响应的呢?
- $set的响应式:为什么在这里会无效?
- 可以使用 this.$forceUpdate()进行解决这个问题,它的作用以及是否会产生副作用,有更好的解决方式吗?
当然我Vue的版本是2.6.10
关于错误的点,上面已经说了。
在这里补充一点,代码未触发报错的原因在于,并未直接操作computed数据。
先展示下报错是怎样的:
代码中写的是
this.commentData[index].commentLiked = true
, 这里可以看做3部分this.commentData[index]
.commentLiked 调用了this.commentData[index]
的getthis.commentData[index]
.commentLiked = true调用了this.commentData[index]
的set因此,并未直接调用this.commentData的set,所以未触发报错。
如果要触发这个报错,怎么去做呢?
如此,直接调用commentData的set方法,便会触发vue内部的报错。
问题解析
问题1
这句话说的不够准确。
如果说要通过修改index, 那么vue 可以通过修改index 实现数据改变。
那所谓的vue数组问题,应该是什么呢?
直接修改调用arr.set方法,而Object.defineProperty又无法监听数组长度的变化,故这里数据会发生改变,但无法触发页面渲染。
使用computed后(问题1生效,问题2不生效)
初始化数据时,computed 会遍历内部值,通过Object.defineProperty监听每个值,并会执行new Watcher,简写代码如下:
故方案一调用
this.list[key]
.name, 调用前半部分触发get,返回arr的值,此时就变成了
this.arr[key].name
而arr位于data中,它本身也被绑定成响应式数据,故这种就跟上面的例子的原因是一致的。
方案二调用
this.list[key]
,调用前半部分触发get,返回arr的值此时就变成了
this.arr[key]={name: "Alias"}
而这里直接通过数组下标修改数组内的值,故数据改变,页面不渲染
这里target 为this.list,即为数组,故这里调用target.splice修改数据,这就解释了数据为什么改变了。
剩下的就是页面未渲染。
虽说vue重写了数组中部分方法,但重写过程仅在
Observer
生成响应式数据中可用(即data等),而computed,未调用Observer,其内部直接调用了Object.defineProperty和Watcher方法,故这里的splice为数组的原生方法,因此这里并不会触发页面渲染,也就解释了set无效的原因。问题3
$forceUpdate
迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
针对于上面未页面未重新渲染的地方,$forceUpdate可用强行触发更新。
但强行触发更新,会触发实例本身所有数据重新更新,也存在性能浪费问题。
一般来说,尽可能从代码角度避免使用该方法,以及针对性进行性能优化。
但是,一旦组件变得复杂起来,该方法有时也会必不可少。对于这种情况,Welcome to Vue3。
以上纯属个人理解,如有错误,欢迎指出,谢谢!