设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 重新 试卷 文件
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

深入理解React的Virtual DOM(3)

发布时间:2019-04-24 21:27 所属栏目:21 来源:Choerodon
导读:▌Case 2:type仍然是相同的字符串,props是不同的。 //beforeupdate: {type:'div',props:{className:'cn'}} //afterupdate: {type:'div',props:{className:'cnn'}} 由于type仍然代表一个HTML元素,React知道如何通

▌Case 2:type仍然是相同的字符串,props是不同的。

  1. // before update:  
  2. { type: 'div', props: { className: 'cn' } }  
  3. // after update:  
  4. { type: 'div', props: { className: 'cnn' } } 

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

▌Case 3:type已更改为不同的组件String或从String组件更改为组件。

  1. // before update:  
  2. { type: 'div', props: { className: 'cn' } }  
  3. // after update:  
  4. { type: 'span', props: { className: 'cn' } } 

由于React现在看到类型不同,它甚至不会尝试更新DOM节点:旧元素将与其所有子节点一起被删除(unmount)。因此,在DOM树上替换完全不同的元素的代价会非常之高。幸运的是,这在实际情况中很少发生。

重要的是要记住React使用===(三等)来比较type值,因此它们必须是同一个类或相同函数的相同实例。

下一个场景更有趣,因为这是开发者最常使用React的方式。

▌Case 4:type是一个组件。

  1. // before update:  
  2. { type: Table, props: { rows: rows } }  
  3. // after update:  
  4. { type: Table, props: { rows: rows } } 

你可能会说,“这好像没有任何变化”,但这是不对的。

如果type是对函数或类的引用(即常规React组件),并且启动了树diff比较过程,那么React将始终尝试查看组件内部的所有child以确保render的返回值没有更改。即在树下比较每个组件 - 是的,复杂的渲染也可能变得昂贵!

组件中的children

除了上面描述的四种常见场景之外,当元素有多个子元素时,开发者还需要考虑React的行为。假设有这样一个元素:

  1. // ...  
  2. props: {  
  3.   children: [  
  4.       { type: 'div' },  
  5.       { type: 'span' },  
  6.       { type: 'br' }  
  7.   ]  
  8. },  
  9. // ... 

开发者开发者想将它重新渲染成这样(span和div交换了位置):

  1. // ...  
  2. props: {  
  3.   children: [  
  4.     { type: 'span' },  
  5.     { type: 'div' },  
  6.     { type: 'br' }  
  7.   ]  
  8. },  
  9. // ... 

那么会发生什么?

当React看到里面的任何数组类型的props.children,它会开始将它中的元素与之前看到的数组中的元素按顺序进行比较:index 0将与index 0,index 1与index 1进行比较,对于每对子元素,React将应用上述规则集进行比较更新。在以上的例子中,它看到div变成一个span这是一个情景3中的情况。但这有一个问题:假设开发者想要从1000行表中删除第一行。React必须“更新”剩余的999个孩子,因为如果与先前的逐个索引表示相比,他们的内容现在将不相等。

幸运的是,React有一种内置的方法来解决这个问题。如果元素具有key属性,则元素将通过key而不是索引进行比较。只要key是唯一的,React就会移动元素而不将它们从DOM树中移除,然后将它们放回(React中称为挂载/卸载的过程)。

  1. // ...  
  2. props: {  
  3.   children: [ // 现在react就是根据key,而不是索引来比较了  
  4.     { type: 'div', key: 'div' },  
  5.     { type: 'span', key: 'span' },  
  6.     { type: 'br', key: 'bt' }  
  7.   ]  
  8. },  
  9. // ... 

当状态改变时

到目前为止,本文只触及了props,React哲学的一部分,但忽略了state。这是一个简单的“有状态”组件:

  1. class App extends Component {  
  2.   state = { counter: 0 }  
  3.   increment = () => this.setState({  
  4.     counter: this.state.counter + 1,  
  5.   })  
  6.   render = () => (<button onClick={this.increment}>  
  7.     {'Counter: ' + this.state.counter}  
  8.   </button>)  

现在,上述例子中的state对象有一个counter属性。单击按钮会增加其值并更改按钮文本。但是当用户点击时,DOM会发生什么?它的哪一部分将被重新计算和更新?

调用this.setState也会导致重新渲染,但不会导致整个页面重渲染,而只会导致组件本身及其子项。父母和兄弟姐妹都可以幸免于难。

修复问题

本文准备了一个DEMO,这是修复问题前的样子。你可以在这里查看其源代码。不过在此之前,你还需要安装React Developer Tools。

(编辑:ASP站长网)

网友评论
推荐文章
    热点阅读