Appearance
PureComponent
本节目标:理解“PureComponent”的核心思路,并能把它用于实际开发或面试表达。
说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。
- 当一个组件的
props或state变更,React会将最新返回的元素与之前渲染的元素进行对比,以此决定是否有必要更新真实的DOM,当它们不相同时React会更新该DOM; - 如果渲染的组件非常多时可以通过覆盖生命周期方法
shouldComponentUpdate来进行优化; shouldComponentUpdate方法会在重新渲染前被触发。其默认实现是返回true,如果组件不需要更新,可以在shouldComponentUpdate中返回false来跳过整个渲染过程。其包括该组件的render调用以及之后的操作;
重复渲染
importReact,{ Component } from
'react';
importReactDOM from
'react-dom';
classApp extends Component {state = {counter: { number: 0 }}add = () =\> {let oldState =
this.state; `let` `amount` `=`
parseInt(this.amount.value); `let` `newState` `=` `{`
...oldState, `counter:` `amount` `==` `0`
`?`
oldState.counter `:` `{` `number:`
oldState.counter.number+ amount }};
this.setState(newState);}
render(){
console.log('Apprender'); `return` `(`
<div><Countercounter={this.state.counter}/\>
<inputref={inst=\>
this.amount=
inst}/\>
<buttononClick={this.add}>+</button></div>)}
}
classCounter extends
React.Component{
render(){
console.log('Counterrender'); `return` `(`
<p>{this.props.counter.number}</p> `)`
}
}
ReactDOM.render(<App/>,document.getElementById('root')
)PureComponent
React15.3中新加了一个类PureComponent,它会在render之前帮组件自动执行一次shallowEqual(浅比较),来决定是否更新组件;PureComponent通过prop和state的浅比较来实现shouldComponentUpdate
import
React,{ Component } from
"react";
importReactDOM from
"react-dom";
classPureComponent extends Component {
shouldComponentUpdate(newProps){return
!shallowEqual(this.props,newProps);}
}
functionshallowEqual(obj1,obj2){if
(obj1===
obj2){return
true;}if
(typeofobj1 != "object" || obj1 === null || typeof obj2 != "object" || obj2 ===
null){return
false;}let keys1 =
Object.keys(obj1);let keys2 =
Object.keys(obj2);if
(keys1.length!=
keys2.length){return
false;}for
(letkey of
keys1){if
(!obj2.hasOwnProperty(key)||
obj1[key]!==
obj2[key]){return
false; `}`
}return
true;
}
classApp extends Component {state = { counter: { number: 0 } };add = () =\> {let oldState =
this.state; `let` `amount` `=`
parseInt(this.amount.value); `let` `newState` `=` `{`
...oldState, `counter:`
`amount` `==` `0`
`?`
oldState.counter `:` `{` `number:`
oldState.counter.number+ amount }};
this.setState(newState);};
render(){
console.log("Apprender"); `return` `(`
<div><Countercounter={this.state.counter}/\>
<inputref={inst=\>
(this.amount=
inst)}/\>
<buttononClick={this.add}>+</button></div> `);`
}
}
classCounter extends PureComponent {
render(){
console.log("Counterrender"); `return`
<p>{this.props.counter.number}</p>;}
}
ReactDOM.render(<App/>,document.getElementById("root")
);PureComponent+Immutable.js
Immutable.js是
Facebook在2014年出的持久性数据结构的库Immutable Data就是一旦创建,就不能再被更改的数据。对Immutable对象的任何修改或添加删除操作都会返回一个新的Immutable对象;Immutable实现的原理是Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变,同时为了避免deepCopy把所有节点都复制一遍带来的性能损耗;Immutable使用了Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享
immutable
immutable-js内部实现了一套完整的
Persistent Data Structure,还有很多易用的数据类型。像Collection、List、Map、Set、Record、Seq
安装
cnpm install immutable -S使用
let{ Map } =
require("immutable");
constmap1 =
Map({a: { aa: 1 }, b:
2,c: 3
});
constmap2 =
map1.set('b',50);
console.log(map1!==
map2);
//true
console.log(map1.get('b'));
//2
console.log(map2.get('b'));
//50
console.log(map1.get('a')===
map2.get('a'));
//true
重构
importReact,{ Component } from
"react";
importReactDOM from
"react-dom";
import{
Map,is } from
"immutable";
classPureComponent extends Component {
shouldComponentUpdate(newProps){return
!shallowEqual(this.props,newProps);}
}
functionshallowEqual(obj1,obj2){if
(obj1===
obj2){return
true;}if
(typeofobj1 != "object" || obj1 === null || typeof obj2 != "object" || obj2 ===
null){return
false;}let keys1 =
Object.keys(obj1);let keys2 =
Object.keys(obj2);if
(keys1.length!=
keys2.length){return
false;}for
(letkey of
keys1){if
(!obj2.hasOwnProperty(key)||
!is(obj1[key],obj2[key])){return
false; `}`
}return
true;
}
classApp extends Component {state = { counter:
Map({number: 0 }) };add = () =\> {/**let oldState = this.state;let amount = parseInt(this.amount.value);this.setState({counter:{ number: oldState.counter.number amount }});*/
this.state.counter=
this.state.counter.set('number',this.state.counter.get('number')+
parseInt(this.amount.value) `);`
this.setState(this.state);};
render(){
console.log("Apprender"); `return` `(`
<div><Countercounter={this.state.counter}/\>
<inputref={inst=\>
(this.amount=
inst)}/\>
<buttononClick={this.add}></button></div> `);`
}
}
classCounter extends PureComponent {
render(){
console.log("Counterrender"); `return`
<p>{this.props.counter.number}</p>;}
}
ReactDOM.render(<App/>,document.getElementById("root"));