react 性能优化 虚拟dom

jsx的背后

React开发使用一种称为JSX的语法,混合了HTML和JavaScript。但浏览器对JSX及其语法毫无头绪,浏览器只能理解纯碎的JavaScript,所以JSX必须转换成JavaScript。这里是一个div的JSX代码,它有一个class name和一些内容:

	<div className='cn'>
	  Content!
	</div>

被转化成JavaScript代码:

React.createElement(
  'div',
  { className: 'cn' },
  'Content!'
);

他有三个参数:

  • 1、type -> 标签
  • 2、attributes -> 标签属性,没有的话,可以为null
  • 3、children -> 标签的子节点
    这段代码在调用render时候执行,返回一个element
{
   type:'div'
   props:{
        className:'cn'
   }
    children: [
      'Content 1!',
    ]
}

在构建虚拟DOM对象完成之后,ReactDOM.render将会按下面的原则,尝试将其转换为浏览器可以识别和展示的DOM节点:

  • 如果type包含一个带有String类型的标签名称(tag name)—— 创建一个标签,附带上props下所有attributes。

  • 如果type是一个函数(function)或者类(class),调用它,并对结果递归地重复这个过程。

  • 如果props下有children属性 —— 在父节点下,针对每个child重复以上过程。

虚拟DOM算法的思想

标准的DOM机制是用户在应用上的操作实际反映的是直接对真实的dom进行操作,而在React中,用户在应用中对dom的操作实际上是对虚拟dom的操作,用户的操作产生的数据改变或者state变量改变都会应用到虚拟dom上,之后再批量的对这些更改进行diff算法计算,对比操作前后的虚拟dom树,把更改后的变化再同步到真实dom上。(在虚拟dom上执行多次修改,在真实dom中,只会执行一次dom操作,因为在React虚拟dom机制中,它会把所有的操作都合并,只会对比刚开始的状态和最后操作的状态,两者中找出不同,然后再同步到真实dom中。)

重新构建DOM

在实际应用场景,render通常在根节点调用一次,后续的更新会有state来控制和触发调用。

场景1:type是一个字符串,type在通话中保持不变,props也没有改变。
// before update
{ type: 'div', props: { className: 'cn' } }

// after update
{ type: 'div', props: { className: 'cn' } }
场景2:type仍然是相同的字符串,props是不同的。
// before update:
{ type: 'div', props: { className: 'cn' } }

// after update:
{ type: 'div', props: { className: 'cnn' } }

type仍然代表HTML元素,React知道如何通过标准DOM API调用来更改元素的属性,而无需从DOM树中删除一个节点。

场景3:type已更改为不同的String或从String组件。
// before update:
{ type: 'div', props: { className: 'cn' } }

// after update:
{ type: 'span', props: { className: 'cn' } }

React看到的type是不同的,它甚至不会尝试更新我们的节点:old元素将和它的所有子节点一起被删除(unmounted卸载)。因此,将元素替换为完全不同于DOM树的东西代价会非常昂贵。幸运的是,这在现实世界中很少发生。

划重点,记住React使用===(triple equals)来比较type的值,所以这两个值需要是相同类或相同函数的相同实例。

Diff算法(逐层比较)

Diff算法也叫同级比较算法,是逐层比较的,一旦发现某个节点没了,就删除,发现新增了一个节点,那就新增一个节点,发现该位置该存在,就原地保留该节点。但是标准的Diff算法复杂度需要O的三次方,这样的算法显示是无法满足性能要求的,所以势必要对该算法进行简化的。React 通过制定大胆的策略,将 O(n^3) 复杂度的问题转换成 O(n) 复杂度的问题。

性能优化
1.bind函数

三种方法

constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this); //构造函数中绑定
}
//然后可以
<p onClick={this.handleClick}>
<p onClick={this.handleClick.bind(this)}>
<p onClick={() => { this.handleClick() }}>

哪一个好呢?
显然是第一个,因为因为第一种,构造函数每一次渲染的时候只会执行一遍;
而第二种方法,在每次render()的时候都会重新执行一遍函数;
第三种方法的话,每一次render()的时候,都会生成一个新的箭头函数

优化Mounting / Unmounting
<div>
  <Message />
  <Table />
  <Footer />
</div>

如果用户读了 Message 之后,少了一项 Message
React会怎么处理呢?它会看作是一个array类型的children,现在少了第一项,从前第一项是Message现在是Table了,也没有key作为索引,比较type的时候又发现它们俩不是同一个function或者class的同一个实例,于是会把整个Table unmount,然后在mount回去,渲染它的1000+行子数据。
因此我们可以使用短路求值(又名“最小化求值”)

<div>
  {isShown && <Message />}
  <Table />
  <Footer />
</div>

当 Message 不显示以后,父元素div的props.children仍然会拥有三个元素,所以不用把整个Table unmount,然后在mount回去。

PureComponent

当你肯定一个分支不会有更新的时候,你可以写shouldComponentUpdate方法来让react不重新渲染子组件,从而优化性能

class Title extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    console.log('title render')
    return (
      <div>{this.props.title}</div>
    )
  }
}


class PureCom extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      title: 'pure',
      num: 0
    }
    this.add = this.add.bind(this);
  }
  add() {
    let { num } = this.state;
    num++;
    this.setState({ num })
  }
  render() {
    console.log('pure render')
    return (
      <div>
        <Title title={this.state.title} />
        <p>{this.state.num}</p>
        <button onClick={this.add}>add</button>
      </div>
    )
  }
}
class Title extends React.Component {
  constructor(props) {
    super(props)
  }
  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.title != this.props.title) {
      return true     //只有title变化时才更新
    } else {
      return false
    }
  }
  render() {
    console.log('title render')
    return (
      <div>{this.props.title}</div>
    )
  }
}

当然还有一个更省事的方法,可以如下编写代码

class Title extends React.PureComponent {
  constructor(props) {
    super(props)
  }
  render() {
    console.log('title render')
    return (
      <div>{this.props.title}</div>
    )
  }
}

将 React.Component 换成 React.PureComponent,因为shouldComponentUpdate是被编在PureComponent,省去了写很多shouldComponentUpdate的时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值