vue数据响应的原理分析和解决方案

本文讨论了Vue.js中数组变更检测的问题,尤其是当直接修改数组值或长度时无法触发更新。提供了使用`Vue.set`、`splice`和深拷贝数组的方法来解决这一问题,同时介绍了对象属性添加和删除的响应性限制以及应对策略。
摘要由CSDN通过智能技术生成

问题描述

赋值之后不能立马更改数据 点击其他地方才能触发渲染


原因分析:

某个渲染的数组改变了层级较深的数据导致页面没有实时渲染

数组变更检测注意事项:
由于 JavaScript 的限制,Vue 不能检测以下数组的变动:

  • 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  • 当你修改数组的长度时,例如:vm.items.length = newLength

解决方案:

不可直接赋值 先取出值 注意使用解构语法拷贝数组[…xxx]

初始:

itBattery(code, num, battery) {
        this.batteryPackDialog=false
        this.$refs.detailList.tableData.BATTERY_PACKAGE_CODE=code
  },

改进:

itBattery(code, num, battery) {
      const list=[...this.$refs.detailList.tableData]
        this.batteryPackDialog=false
        list[this.curSelect].BATTERY_PACKAGE_CODE=code
        this.$refs.detailList.tableData =[... list];
  },


拓展:

首先我们先来了解vue数据响应的原理。官方文档的解释:

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。

也就是说当改变data中属性的值时会触发其相应setter的调用,从而实现响应的操作。但getter和setter是有局限性的。我们先来看下面的这个例子:

复制代码

var person = {};
Object.defineProperties( person, {
age: {
defaultValue: 11,
get: function () {
return this.defaultValue;
},
set: function (val) {
this.defaultValue = val;
console.log(“触发了set”)
}
}
});

// 修改属性的值时能够触发set
person.age = 12 // 触发了set
->触发了set
->12

person.age
->12

// 将属性的值设置为一个数组,当通过索引值修改数组的某一项或使用数组的某些方法修改数组时不能触发set
person.age = [2,3,4] // 触发了set
->触发了set
->(3) [2, 3, 4]

person.age[2] = 5 // 未触发set
->5

person.age
->(3) [2, 3, 5]

person.age.push(5) // 未触发set
->4

person.age
->(4) [2, 3, 4, 5]

// 将属性的值设置为一个对象,当修改对象中某属性的值时无法触发set
person.age = { first: 1 }
->触发了set
->{first: 1}

person.age.first = 2 // 未触发set
->2
复制代码

通过上述例子可以观察得出:

当该属性的值为一个数组时,通过索引修改数组某一项的值或使用数组的某些方法修改数组并不能触发set;当属性的值为一对象时,直接修改对象中属性的值时也无法触发set。

为了解决当你利用索引直接设置一个数组项问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新:

// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:

vm.$set(vm.items, indexOfItem, newValue)
为了解决当你修改数组的长度问题,你可以使用 splice:

vm.items.splice(newLength)

例子:

event: (val)=>{
                // 不能直接赋值
                this.$refs.detailList.tableData[str] ={
                  ...this.$refs.detailList.tableData[str],
                  placeCode:’xxx’ 
                } ;
                console.log('event',str,val,placeOptions,data)
              },


改进:

event: (val)=>{
                
                const obj ={
                  ...this.$refs.detailList.tableData[str],
                  placeCode:’xxx’
                } ;
                this.$refs.detailList.tableData.splice(str,1,obj)//split方法替换数组
                console.log('event',str,val,placeOptions,data)
              },



对象变更检测注意事项:

还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:

复制代码

var vm = new Vue({
data: {
a: 1
}
})
// vm.a 现在是响应式的

vm.b = 2
// vm.b 不是响应式的
复制代码

对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。例如,对于:

复制代码

var vm = new Vue({
data: {
userProfile: {
name: ‘Anika’
}
}
})
复制代码

当修改对象的属性或为对象添加属性时,应该使用以下方法:

Vue.set(vm.userProfile, ‘age’, 27)
或者

vm.$set(vm.userProfile, ‘age’, 27)
有时你可能需要为已有对象赋值多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样:

Object.assign(vm.userProfile, {
age: 27,
favoriteColor: ‘Vue Green’
})
你应该这样做:

vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: ‘Vue Green’
})
由于数据响应原理机制, Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明所有可能用到的根级响应式属性,且为这些属性都设一个初值,哪怕只是一个空值。

回归正题,我项目中遇到的这个问题,解决方法:

  1. 运用this.$forceUpdate()强制刷新。

  2. 使用vm.$set(vm.items, indexOfItem, newValue)

eg. vm.$set(vm.dataList[i], picUrl, ‘data:image/jpg;base64,’ + response.data)

export default {
data() {
return {
dataList:[],
};
},
methods: {
getData() {
var _this = this;
var dataList = [];
dataList = response.data.data;

  for(var i=0;i<dataList.length;i++){
     _this.downloadBase64(dataList[i].fielID, i, dataList);
  }

},
downloadBase64(fielID, i, dataList) {
    var vm = this;
    $.ajax({
        url: AppInter.downloadBase64,
        asysc: true,
        data: {
            fileName: '1.jpg',
            fileId: fielID
        },
        cache: !0,
        timeout: 2e4,
        dataType: "json",
        type: "POST",
        xhrFields: {
            withCredentials: !0
        },
        crossDomain: !0,
        contentType: "application/x-www-form-urlencoded;charset=UTF-8",
        beforeSend: function () {
            vm.loading = weui.loading();
        },
        success: function (response) {
            if (response.responseCode == '0') {
                dataList[i].picUrl = 'data:image/jpg;base64,' + response.data;
                vm.dataList = dataList;
                vm.$forceUpdate();
            } else {
                weui.alert("请求错误,请稍候再试", function () {}, {
                    title: '温馨提示'
                });
            }
        },
        error: function (xhr, msg, err) {
            weui.alert("请求失败,请稍候再试", function () {}, {
                title: '温馨提示'
            });
        },
        complete: function () {
            if (vm.loading.hide) {
                vm.loading.hide();
            }
        }
    })
}

},
created() {

},
mounted() {

}
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值