vue的数据更新一般有3种方式
1.页面的接口刷新
直接调取刷新的接口,重刷数据即可
2.this.$set()
vue的数据更新方式是有缺点的,我们都知道,vue的双向数据绑定通过Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变。这个也就是mvvm的原理。
function getReactive(data, key, value) {
Object.defineProperty(data, key, {
get() {
console.log('getter:读取拦截器 拦截了 对象的a属性 的获取值的操作')
return value
},
set(newValue) {
if(newValue === value) return;
console.log('设置拦截器 拦截了 对象的b属性 的设置值的操作')
value = newValue;
}
})
}
const data = {
a: '1',
b:'2'
}
let keys = Object.keys(data);
for(let i = 0; i< keys.length; i++) {
let key = keys[i];
let value = data[key];
// 把data中的每一项变成响应式数据
defineReactive(data,key, value)
}
console.log(data.a);
data.b= 'nihao'
但是Object.defineProperty()
这个api本身是有缺陷的
Object.defineProperty
的缺陷
a.无法监控到数组下标的变化,通过数组下标修改元素,无法实时响应
var arr = [1,2,3,4,5]
arr[0] = 5 // 这种操作无法进行视图更新,可以更新数据
// 另外,如果是在vue中定义的arr,则使用this.arr[0] = 5 会失去响应性
b.只能劫持对象的属性,对象属性的新增和删除则无法劫持到,另外对象属性如果嵌套过深,使用this.$set()
更新有可能会不生效。
var obj = {name:'li',info:{age:20,sex:'男'}}
obj.info.age = 25 // 这种操作无法进行视图更新,可以更新数据
// 同上
所以vue3后来改用了proxy
a.Proxy 可以劫持整个对象,并返回一个新的对象
b.Proxy 不仅可以代理对象,还可以代理数据,可以代理动态增加的属性
于是,后来为了解决这种这些bug,采取了两种措施。
首先是数组,对数组的部分进行重写,使用以下的方法一样可以在数组的操作之后进行视图的更新push
pop
shift
unshift
splice
sort
reverse
,其次就是使用this.$set()
方法。对象好像没有对某些api进行过重写,所以好像只有this.$set()方法。
this.set()
方法的使用
调用方法:this.$set( target, key, value )
target:要更改的数据源(可以是对象或者数组)
key:要更改的具体数据
value :重新赋的值
//数组
var arr = [0,1,2,,4,5]arr[0] = 5 => this.$set(arr,0,5)
// 对象
var obj = {id:1,name:wang,age:28}
obj.age = 25 => this.$set(obj,'age',25)
3.this.$forceUpdate()
强制渲染
当我们真正使用this.$set()
有时候会发现,这个方法有时候也不是那么好用,当你的组件嵌套过深,也就意味着组件内的数据也是如此,有时候在使用this.$set()
就发现不起作用了,那么到了这个时候,就不要关心什么性能了,先把功能完成吧。这个时候使用this.$forceUpdate()
是绝对没错的
场景:在一个孙子组件中,使用el-checkbox做一个类似于表格的全选功能
<template>
<el-button @click="checkAll(checkFlag)">全选</el-button>
<ul>
<li
v-for="(item, index) in imgListData"
:key="index"
>
<div class="title">
<el-checkbox
v-model="item.checked"
label=""
name="type"
@change="getCheckBox">
</el-checkbox>
</div>
</li>
</ul>
</template>
<script>
data(){
return{
imgListData:[
{id:1,checked:false},
{id:2,checked:false},
{id:3,checked:false},
{id:4,checked:false}],
checkFlag:false
}
},
methods:{
getCheckBox(){
// 这个时候发现点击会触发change事件,视图中的checkbox会加上边框,但是不打钩
// 于是在这里强制刷新,解决问题
this.$forceUpdate()
},
checkAll(checkFlag){
// 这里也是一样,通过this.$set()状态控制checked让checkbox打钩不管用,于是强制更新
if(checkFlag){
this.imgListData.forEach(async (ele,i) => {
// this.$set(this.imgListData[i],"checked",false)
this.imgListData[i].checked = false });
this.checkFlag = false
} else {
this.imgListData.forEach(async (ele,i) => {
// this.$set(this.imgListData[i],"checked",true)
this.imgListData[i].checked = true });
this.checkFlag = true
}
this.$forceUpdate();
}
}
</script>