deep-in-vue
deep-in-vue copied to clipboard
谈谈get/set与getState/setState的优劣
vue数据响应式特性
vue通过Object.defineProperty方法实现对数据更改的监测来实现数据的响应式,这是vue比较让人着迷的一个特性,刚接触vue的时候真的太为它着迷了,但也有它自身的问题。
Object.defineProperty简要说明:
function bindAccessors (vm, key, binding) {
Object.defineProperty(vm, key, {
get: function () {
return binding.value;
},
set: function (value) {
binding.value = value;
binding.directives.forEach(function (directive) {
directive.update(
directive.el,
value,
directive.argument,
directive,
vm,
key
)
})
}
})
}
如上所示,通过对vm对象的key属性set操作的拦截,实现了vm.key属性的监测和响应,当vm[key] = 1代码片段执行的时候,会监测到该属性的赋值操作(这里不能说监测到值的更改,因为如果原有值也为1的话,那么就不能算更改,因为最后的值为1),然后执行directive.update更新操作。
get/set监测数据更新存在的问题
(这里并不是说vue响应式特性存在的问题,因为vue在此基础上进行了不少的优化,这里讨论的是set/get监测数据存在的问题,毕竟优化问题和消灭问题区别还是很大的)
- 频繁的set操作会造成更新的浪费 在人眼可感知的阈值内的set操作可能会是不必要的更新资源浪费。
- 可能会出现无意义的更新操作 如下所示:
var data = {
value: 1
}
data.value = 1;
data.value = 1;
如果两次set的值是一样的,那么会触发两次作用一样的更新操作,无疑造成了资源的浪费。
如果想通过比较连续两次值是否相等来过滤掉其中一次无意义的更新操作所带来的成本是很大的,因为属性的值可能会是对象、函数、数组等复合对象,要判断此次更新对页面是否有副作用是很困难的。
- 数组、对象子属性的响应问题 数组的push/pop/unshift/shift/filter等操作都需要单独处理,因为set是无法监测这些操作的; 执行了set的属性的子属性如果没有进行set的处理那么是没有响应式特性的; (vue在这点的处理上还是比较好的,基本解决了这两个问题。)
getState/setState式更新
这种方式的更新不是监测赋值操作而是将触发的时机交给了开发者.
因为需要开发者手动更新,所以与get/set的方式相比会显得繁琐,但是却没有上面提及的那几个问题(复合对象值比较的问题依然不能解决,但是可以将其留给开发者来决定是否需要更新)。
如果想通过比较连续两次值是否相等来过滤掉其中一次无意义的更新操作所带来的成本是很大的,因为属性的值可能会是对象、函数、数组等复合对象,要想对它们进行值的比较是很困难的。
用 === 比较过滤相同值完全没问题,getter/setter 的问题不在这,在于的一点比如如果需要获取 oldValue 的话才是比较费成本的操作。
这里其实想表达的是同一个意思,值的比较主要是针对原子类型来的,对象的直接比较是没什么意义的,对象存储的是引用而不是值,所以如果对一个响应式对象进行重新赋值后,所需要对对象进行的处理以及计算与原有对象相比对页面的影响这一步的成本是很大的。