总结:
无论是PureRender还是key值,整个React组件的优化逻辑都是针对Virtual DOM的更新优化。
1.纯函数的三大原则
- 给定相同的输入,他总是返回相同的输入
- 过程没有副作用(不能改变外部状态)
- 没有额外的状态依赖(方法内的状态只在方法的生命周期中存活)
2.PureRender
1.PureRender本质
- 只对新旧props和state做了浅比较(只做了引用)比较,没有做值的比较);如果返回false,组件就不执行render方法,默认情况返回true。react-addons-pure-render-mixin插件允许我们使用PureRender。
源码:
function shallowEqual(obj,newObj){
if(obj===newObj){
return true;
}
const objKeys=Object.keys(obj);
const newObjKeys=Object.keys(newObj);
if(objKeys.length!==newObjKeys.length){
return false;
}
//只需关注props中的每一个是否相等,无需深入判断。
return objKeys.every(key=>{
return newObj[key]===obj[key]
});
}
2.在组件中使用:
import React,{component} from ‘react’;
import PureRenderMixin from ‘react-addons-pure-render-mixin’;
class App extends Component{
constructor(props){
super(props);
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
render(){
return <div calssName = {this.props.className}>foo</div>;
}
}
3.优化PureRender
1)不能直接为props设置对象或数组;
需要提前将赋值成常量,不直接使用字面量。因为我们每次调用React组件其实都会重新创建组件,就算传入的数组或对象没有改变,但是他们的引用地址还是会发生变化。
2)设置props方法并通过实践绑定在元素上
我们不用每次都绑定事件,可以将它绑定移到构造器中
3)设置子组件
对于设置了子组件的React组件,再调用shouldComponentUpdate时,均返回true;如何避免子组件重复渲染,就是在他的父组件中设置
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
3.Immutable
由于js一般对象赋值都是引用赋值,所以一般都是可变的,这样改变一个值,原来的值也会发生变发。因此是可变的(mutable)。
为了解决这个问题。
-1.使用浅拷贝或深拷贝来避免被修改。
-2. immutable数据是一旦创建,就不能在更改的数据,对Immutable进行修改,删除,添加都会返回一个新的Immutable对象。也就是Immutable原理就是持久化的数据结构,也就是使用旧数据创建新数据的时,要保证旧数据同时可用且不可变。Immutable数据使用了结构共享,即如果对象树中一个节点变化,只修改这个节点和受她影响的父节点,其他节点则进行共享。
Immutable.js简介:
- 1.Immutable优点
1)降低了“可变”带来的复杂度
2)节省内存;实现结构共享,没有被引用的对象对被垃圾回收。
3)撤销/重做,复制/粘贴,甚至时间旅行这些功能做起来都是小菜一碟。因为每次数据都是不一样的,只要把它们存到一个数据中,想退回哪里就拿出来就可以了,不用担心会发生变化。
4)并发安全。但是js是单线程的,所以目前没什么用。
5)函数式编程。只要输入一直,输出必然一致。 - 2.Immutable缺点
容易与原生的对象混淆。
避免该缺点
1)使用FlowType或TypeScript静态类型检查工具
2)约定变量命名规则,如所有Immutable对象都用$$开头
3)使用Immutable.fromJS而不是用Immutable.Map或Immutable.List来创建对象。这样可以避免Immutable对象和原生对象间的混用。 - 3.Immutable.is
比较两个Immutable对象的值用的是Immutable.is(a,b);"值比较";
import Immutable from 'immutable';
let a=Immutable.fromJS({a:1,b:2,c:3});
let b=Immutable.fromJS({a:1,b:2,c:3});
console.log(1,a===b);//false
console.log(2,Immutable.is(a,b));//true
- 4.Immutable和cursor
cursor提供了可以直接访问这个深层数据的引用。
import Immutable from 'immutable';
let data=Immutable.fromJS({a:{b:{c:1}}});
let cursor=Cursor.from(data,['a','b'],newData=>{console.log(newData)});
console.log(1,cursor.get('c'));//1
cursor=cursor.update('c',x=>x+1);
console.log(2,cursor.get('c'));//2
- 5.Immutable与PureRender
提供了is和===比较重写了shouldComponentUpdate;
使用immutable data之后,仅仅改变状态了的组件及其父组件被重新渲染。
import React,{ commponent } from 'react';
import { is } from 'immutable';
class App extends Component {
shouldComponentUpdate(nextProps, nextState){
const thisProps = this.props || {};
const thisStae = this.state || {};
if (Object.keys(thisProps).length !== Object.keys(nextProps).length ||
Object.keys(thisState).length !== Object.keys(nextState).length){
return true;
}
for (const keys in nextProps){
// !==判断原生对象,is判断immutable对象
if (thisProps[key] !== nextProps[key] ||
!is(thisProps[key], nextProps[key]))
return true;
}
for (const key in nextState){
if ( thisStae[key] !== nextState[key])||
!is(thisStae[key],nextState[key])
return true;
}
}
}
- 6.Immutable和setState
React建议把this.state当做不可变的,因此修改钱需要做一个深拷贝。
import React,{component} from ‘react’;
import {Map}from 'immutable';
class App extends Component{
constructor(props){
super(props);
this.state={
data:Map({time:0})
}
}
handleAdd(){
this.setState(({data})=>({data:data.update('time',v=>v+1)}));
console.log(this.state.data.get('time'));//这里的time是修改前的值0;
}
}
4.key
1)如果子组件是一个数组或迭代器,那么必须有一个唯一的key prop;
2)注意设置key值不要使用index或随机值。因为这个index不是唯一的而且还会变化。
3)当key相同是,只渲染第一个相同key的项,且会报一个警告。
网友评论