React 速度很快!部分速度来自于仅更新 DOM 中需要更新的部分。您无需担心太多,并且可以获得速度提升。只要您了解 setState()
的工作原理,就可以顺利进行。但是,熟悉这个强大的库如何更新应用程序的 DOM 也很重要。了解这一点将对您作为 React 开发人员的工作起到至关重要的作用。
DOM 是什么?
浏览器通过解析您编写的代码来构建 DOM,它在渲染页面之前执行此操作。 DOM 将页面中的文档表示为节点和对象,提供了一个接口,以便编程语言可以插入并操作 DOM。DOM 的问题在于它没有针对动态 UI 应用程序进行优化。因此,当需要更改很多内容时,更新 DOM 会降低应用程序的速度;因为浏览器必须重新应用所有样式并渲染新的 HTML 元素。即使没有任何更改,也会发生这种情况。
什么是协调机制?
协调机制是 React 更新 DOM 的过程。当组件的 状态发生变化 时,React 必须计算是否有必要更新 DOM。它通过创建一个虚拟 DOM 并将其与当前 DOM 进行比较来实现。在这种情况下,虚拟 DOM 将包含组件的新状态。
让我们构建一个简单的组件来添加两个数字。数字将输入到输入字段中。
查看 Kingsley Silas Chijioke (@kinsomicrote) 在 CodePen 上创建的笔 reconciliation Pen。
首先,我们需要为字段设置初始状态,然后在输入数字时更新状态。组件将如下所示
class App extends React.Component {
state = {
result: '',
entry1: '',
entry2: ''
}
handleEntry1 = (event) => {
this.setState({entry1: event.target.value})
}
handleEntry2 = (event) => {
this.setState({entry2: event.target.value})
}
handleAddition = (event) => {
const firstInt = parseInt(this.state.entry1)
const secondInt = parseInt(this.state.entry2)
this.setState({result: firstInt + secondInt })
}
render() {
const { entry1, entry2, result } = this.state
return(
<div>
<div>
<p>Entry 1: { entry1 }</p>
<p>Entry 2: { entry2 }</p>
<p>Result: { result }</p>
</div>
<br />
<div>
<span>Entry 1: </span>
<input type='text' onChange={this.handleEntry1} />
</div>
<br />
<div>
<span>Entry 2: </span>
<input type='text' onChange={this.handleEntry2} />
</div>
<div>
<button onClick={this.handleAddition} type='submit'>Add</button>
</div>
</div>
)
}
}
在初始渲染时,DOM 树将如下所示;

在第一个输入字段中进行输入时,React 会创建一个新的树。新的树(即虚拟 DOM)将包含 entry1
的新状态。然后,React 将虚拟 DOM 与旧 DOM 进行比较,并根据比较结果找出两个 DOM 之间的差异,并仅对不同的部分进行更新。每次 App
组件的状态发生更改时,都会创建一个新的树——在任一输入字段中输入值时,或单击按钮时。

比较不同的元素
当组件的状态发生变化,导致需要将元素从一种类型更改为另一种类型时,React 会卸载整个树并从头开始构建一个新的树。这会导致该树中的每个节点都被销毁。
让我们看一个例子
class App extends React.Component {
state = {
change: true
}
handleChange = (event) => {
this.setState({change: !this.state.change})
}
render() {
const { change } = this.state
return(
<div>
<div>
<button onClick={this.handleChange}>Change</button>
</div>
{
change ?
<div>
This is div cause it's true
<h2>This is a h2 element in the div</h2>
</div> :
<p>
This is a p element cause it's false
<br />
<span>This is another paragraph in the false paragraph</span>
</p>
}
</div>
)
}
}
在初始渲染时,您将看到 div 及其内容,以及单击按钮如何导致 React 销毁 div 及其内容的树,并改为构建 <p>
元素的树。如果两种情况下我们都有相同的组件,也会发生同样的事情。该组件将与其所属的先前树一起被销毁,并构建一个新的实例。请参阅下面的演示;
查看 Kingsley Silas Chijioke (@kinsomicrote) 在 CodePen 上创建的笔 reconciliation-2 Pen。
比较列表
React 使用键 来跟踪列表中的项目。键有助于它确定项目在列表中的位置。如果列表没有键会发生什么?React 将会变异列表的每个子元素,即使没有新的更改。
换句话说,React 会更改没有键的列表中的每个项目。
这是一个例子
const firstArr = ['codepen', 'codesandbox']
const secondArr = ['github', 'codepen', 'bitbucket', 'codesanbox']
class App extends React.Component {
state = {
change: true
}
handleChange = (event) => {
this.setState({change: !this.state.change})
}
render() {
const { change } = this.state
return(
<div>
<div>
<button onClick={this.handleChange}>Change</button>
</div>
<ul>
{
change ?
firstArr.map((e) => <li>{e}</li>)
:
secondArr.map((e) => <li>{e}</li>)
}
</ul>
</div>
)
}
}
这里,我们有两个数组,它们根据组件的状态进行渲染。React 无法跟踪列表中的项目,因此每次需要重新渲染时,它都必须更改整个列表。这会导致性能问题。
在您的控制台中,您将看到如下警告
Warning: Each child in an array or iterator should have a unique "key" prop.
要解决此问题,您可以为列表中的每个项目添加一个唯一的键。在这种情况下,最好的解决方案是创建一个对象数组,每个项目都有一个唯一的 id
。如果我们使用数组索引,那将是 一种反模式,最终会对我们造成伤害。
const firstArr = [
{ id: 1, name: 'codepen'},
{ id: 2, name: 'codesandbox'}
]
const secondArr = [
{ id: 1, name: 'github'},
{ id: 2, name: 'codepen'},
{ id: 3, name: 'bitbucket'},
{ id: 4, name: 'codesandbox'}
]
class App extends React.Component {
state = {
change: true
}
handleChange = (event) => {
this.setState({change: !this.state.change})
}
render() {
const { change } = this.state
return(
<div>
<div>
<button onClick={this.handleChange}>Change</button>
</div>
<ul>
{
change ?
firstArr.map((e) => <li key={e.id}>{e.name}</li>)
:
secondArr.map((e) => <li key={e.id}>{e.name}</li>)
}
</ul>
</div>
)
}
}
查看 Kingsley Silas Chijioke (@kinsomicrote) 在 CodePen 上创建的笔 reconciliation-3 Pen。
总结
总而言之,以下是了解 React 中协调机制概念的两个主要要点
- React 可以使您的 UI 速度更快,但它需要您的帮助。了解其 协调过程 非常有益。
- React 不会完全重新渲染您的 DOM 节点。它只更改需要更改的内容。差异化过程非常快,您可能不会注意到它。
使用数组索引是一种反模式,它只会消除警告,而不会真正解决潜在的性能问题。 https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
使用项目 ID 而不是索引号作为键属性效率更高。这样,当项目被删除时,数组的所有其他键都保持不变。