vue的数据更新

97 阅读1分钟

vue的数据更新一般有3种方式

1.页面的接口刷新

直接调取刷新的接口,重刷数据即可

2.this.$set()

vue的数据更新方式是有缺点的,我们都知道,vue的双向数据绑定通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变。这个也就是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>