react对于虚拟dom的比较是render的前提,开发者可以自定义scu实现渲染优化。然而scu的对象层次比较多,比较会比较耗时。有时候深层次的比较时间反而高于render时间。那么如何优化scu中的比较就是个问题。弄清楚这个之前我们需要知道js的引用类型和基本类型以及深拷贝和浅拷贝是怎么回事。
1,对于字符串类型,浅复制是对值的复制,对于对象来说,浅复制是对对象地址的复制,并没 有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变,而深复制则是开辟新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。
深复制实现代码如下:
可以从两个方法进行解决。
第一种方法、通过递归解析解决
var china = {
nation : '中国',
birthplaces:['北京','上海','广州'],
skincolr :'yellow',
friends:['sk','ls']
}
//深复制,要想达到深复制就需要用递归
function deepCopy(o,c){
var c = c || {}
for(var i in o){
if(typeof o[i] === 'object'){
//要考虑深复制问题了
if(o[i].constructor === Array){
//这是数组
c[i] =[]
}else{
//这是对象
c[i] = {}
}
deepCopy(o[i],c[i])
}else{
c[i] = o[i]
}
}
return c
}
var result = {name:'result'}
result = deepCopy(china,result)
console.dir(result)
第二种方法:通过JSON解析解决
var test ={
name:{
xing:{
first:'张',
second:'李'
},
ming:'老头'
},
age :40,
friend :['隔壁老王','宋经纪','同事']
}
var result = JSON.parse(JSON.stringify(test))
result.age = 30
result.name.xing.first = '往'
result.friend.push('fdagldf;ghad')
console.dir(test)
console.dir(result)
浅复制代码大概如下:
var obj = {
a: 1,
arr: [ 2, 3 ]
};
var shadowObj = shadowCopy( obj );
function shadowCopy( src ) {
var dst = {};
for ( var prop in src ) {
if (src.hasOwnProperty( prop )) {
dst[prop] = src[prop];
}
}
return dst;
}
因为浅复制只会将对象的各个属性进行依次复制,并不会进行递归复制,而 JavaScript 存储对象都是存地址的,所以浅复制会导致 obj.arr 和 shadowObj.arr 指向同一块内存地址,大概的示意图如下。
综上所述,对象的深复制特别需要递归遍历属性复制,当属性嵌套比较深,性能会很差。
对象的比较也是同样的道理。 react 官方给的解决方案是immutable.js 利用对象的不可变性。 每次修改对象的时候,都会返回一个全新的对象,因此可以直接通过=== 判断对象是否相等,从而优化render,那每次生成全新的对象会不会影响性能?请继续往下看。
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。请看下面动画:
也就说每次对immutable对象修改虽然会生成一个不同的对象,但是数据结构是共享的,因此避免了刚才深拷贝带来的性能问题。 一方面提高了性能,另一方面相对于浅拷贝,解决了层级大于一层无法比较的问题
据说这种做法可以显著提高react性能