您的位置:首页 > Web前端 > React

React性能优化——代码篇

2017-10-08 08:30 253 查看
如果使用工具检测出页面浪费的渲染次数太多,就需要检查代码是否写法上有问题了。虽然 
Virtual DOM
 算法可以避免大多无效的真实 
DOM
 操作,但还是会浪费时间在计算不会改变的虚拟 
DOM
 上,也就是执行了 
render
 函数,但发现并没有任何改变。

转载https://wulv.site/2017-07-02/react-perf-code.html


key

key
 属性作为列表的子组件的身份标识,是不能重复的。不写 
key
 属性, 
React
 会在浏览器控制台报 
warning
 ,有时候我们为了去除这个警告,直接使用数组的索引做 
key
 ,这是不太好的。

123456789
class App extends Component {  render() {    return (<div>      {this.props.list.map((value, index) => {        <Child key={index} data={value} />      })}    </div>);  }}
如果 
list
 发生变化,比如从中间去除一条数据,两次渲染之间 
key
 相同的子组件,
React
 认为他们是同一个组件,这样很多个 
Child
 组件都改变了。最好使用一个唯一的
value.id
,如果没有 
id
,使用
value.name
之类的也是可以的, 
key
 不必须是数字,只要保证不重复的字符就可以了。


shouldComponentUpdate

在 
React
 生命周期里, 
shouldComponentUpdate
 表示组件是否需要被更新,我们可以做一些判断,来优化组件性能,让一些确定不会改变视图的操作,直接不去计算。
shouldComponentUpdate
 在初始化时或者使用 
forceUpdate
 时是不被执行的。 
shouldComponentUpdate
 默认返回值是 
true
 ,也就是组件一定会更新,如果返回值为 
false
 ,会阻止后面 
componentWillUpdate
 、 
render
 、 
componentDidUpdate
 等操作。并且如果 
componentWillReceiveProps
 里有 
setState
 操作也会被阻止。

比如我们确定如果一个子组件的 
name
 和 
tel
 属性不变,子组件就不需要更新:

1234567
shouldComponentUpdate(nextProps) {  const { data } = this.props;  const { name, tel } = data;  return name !== nextProps.data.name    || tel !== nextProps.data.tel;}
我们最好只传递 
component
 需要的 
props
 ,如果传得太多,或者层次传得太深,都会加重 
shouldComponentUpdate
 里面的数据比较负担,因此,也请慎用
<Component
{...this.props} />
操作。

123456789
import pick from 'lodash/pick';class App extends Component {  const props = pick(this.props, [max, min, onClick])  render() {    return (<div>      <NumberInput {...props} />    </div>);  }}


PureComponent

PureComponent
 是一个很有效的优化,在前面的博客专门有一篇来介绍:React
PureComponent 使用指南。


Stateless
components

React v0.14
 就添加了 
functional
components
,他的行为很像只有一个 
render
 方法的 
class
components
,但没有生命周期方法,也没有实例对象,所以不能够使用 
ref
。目前来说 
functional
components
 并没有特别优化,在内部也是包装在同一个类中。我们可以直接以函数方式使用,这样可以优化一部分性能:

12345678910111213
const Avatar = (props) => {  return <img src={props.url} />;}// 组件方式使用render() {  return (<div>    <Avatar url={avatarUrl} />  </div>);// 函数方式使用render() {  return (<div>    {Avatar({ url: avatarUrl })}  </div>);
React
 官方已经承诺,在未来会通过避免不必要的检查和内存分配来对这些组件进行性能优化。


constant
elements

我们可以将确定不变的
html
代码抽离出来直接当做一个变量,写在
jsx
里,会解析
jsx
语法,生成
React.createElement()
代码。如果提升成静态元素,
jsx
会直接把它们当做一个值,减少了解析过程:

1234567891011
const _ref = <span>Hello World</span>;class MyComponent extends Component {  render() {    return (      <div className={this.props.className}>        {_ref}      </div>    );  }}
这个操作其实有
babel
插件babel-react-optimize,可以自动帮我们提取。


慎用setState

如果是和视图无关的,但有变化的数据,不要放在 
state
 里面,比如某个组件需要 
mouseDown
 时标记开始记录, 
mouseUp
 清除记录,最好直接当做实例的一个属性。这样可以避免执行无效的 
render
 操作。

1234567891011121314
class App extends Component {  record = false  onMouseDown = () => {    this.record = true;  }  onMouseUp = () => {    this.record = false;  }  render() {    return (<div>      <button onMouseDown={this.onMouseDown} onMouseUp={this.onMouseUp} />    <div/>);  }}
总结一下:
render
 函数里面要用到的东西放
props/state
(影响 
view
),其他的不要放进去,写成模块内的私有变量(跨实例共享)或者组件实例上的变量。

React PureComponent
 使用指南里,复杂状态与简单状态不要共用一个组件那一段也提到了慎用
setState
的原因。


慎用bind

Component
 的 
render
 里不使用 
bind
 绑定 
this
 ,可以放在 
constructor
 里绑定好,或者直接使用箭头函数,如果要动态传参,可以使用闭包,或者可以直接把处理函数传入子组件,子组建时可以拿到参数,再执行父组件的处理函数就可以了。

1234567891011121314151617181920212223242526272829303132333435363738394041
// 闭包class App extends Component {  removeCharacter = index => () => {    const { list } = this.state;    list.splice(index, 1);    this.setState({ list });  }  render() {    return (<div>      {this.state.list.map((value, index) =>        <Child onClick={this.removeCharacter(index)} key={value.id} data={value} />      )}    </div>);  }}// 子组件处理class App extends Component {  removeCharacter = index => {    const { list } = this.state;    list.splice(index, 1);    this.setState({ list });  }  render() {    return (<div>      {this.state.list.map((value, index) =>        <Child onClick={this.removeCharacter} index={index} key={value.id} data={value} />      )}    </div>);  }}class Child extends Component {  handleClick = () => {    const { index, onClick } =this.props;    onClick(index);  }  render() {    return <div onClick={this.handleClick}>      {this.props.data}    </div>  }}
如果每次都在 
render
 里面的 
jsx
 去 
bind
 这个方法,会消耗性能,因为每次
bind
都会返回一个新函数,重复创建静态函数肯定是不合适的(闭包也是这样,但
bind
内部有一系列的算法,比闭包复杂多了)。

关于
bind
性能问题可以查看以下资料:
Will
Function.prototype.bind() always be slow?
Why
is bind slower than a closure?

总的来说,目前浏览器已经足够快了,在
bind
没有成为性能瓶颈之前,这都只是代码写法上的事。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: