我正在学习React,并且偶然发现了“有活力的孩子”这个怪癖。
带有代码示例的序言:
// Render Pass 1 <Card> <p>Paragraph 1</p> <p>Paragraph 2</p> </Card> // Render Pass 2 <Card> <p>Paragraph 2</p> </Card>
在第二render()遍中,vdom diff的工作方式似乎是先删除第二个孩子,然后将第一个孩子的文本转换为“段落2”。速度很快,但是如果您需要状态继续说……第二个孩子,您会看到奇怪的事情发生。
render()
因此,React建议为这些标签使用“键”属性。现在,vdom扩散将毫无意外,您将看到状态被保留renders()。
renders()
我的问题:有没有办法让React自己设置“键”而不是按照他们的建议进行设置?
不,那里没有。
如未观察到的那样,当没有指定键时,React的默认行为是使用天真的方法就地更新组件。从功能上讲,这等效于根据元素相对于其兄弟元素的索引指定默认键。在您的示例中,这看起来像:
// Render Pass 1 <Card> <p key={0}>Paragraph 1</p> <p key={1}>Paragraph 2</p> </Card> // Render Pass 2 <Card> <p key={0}>Paragraph 2</p> </Card>
这就是为什么您看到第一个元素的文本已转换的原因,但是正如您注意到的那样,这并不总是最佳结果。那么为什么React无法使用更智能的自动密钥呢?答案是,为此,实际上只有两个选择,都不是理想的选择:
他们可以对您的应用程序结构做出某些假设。在您的简短示例中,很显然,应该根据段落的文本内容来匹配段落。但这并不一定在所有情况下都是正确的,并且很难将这种逻辑扩展到具有自定义元素,大量道具,嵌套子项等的更复杂的场景。
他们可以使用一种花哨的通用差异算法,但就性能而言,这可能是相当昂贵的,React非常重视。正如React的对帐文档中所说:
有许多算法试图找到最小的操作集来转换元素列表。Levenshtein距离可以使用O(n 2)中的单个元素插入,删除和替换来找到最小值。即使我们要使用Levenshtein,也找不到节点何时移到另一个位置,而这样做的算法的复杂性要差得多。
因此,无论哪种方式,“自动键分配”的任何好处也都带有一系列缺点。最后,这并不是真正值得的,特别是考虑到在大多数情况下,手动分配密钥并不那么困难或麻烦。在同一文档页面的更下方:
在实践中,找到密钥并不难。大多数情况下,您要显示的元素已经具有唯一的ID。如果不是这种情况,则可以向模型添加新的ID属性,也可以对内容的某些部分进行哈希处理以生成密钥。请记住,密钥仅在其兄弟之间必须是唯一的,而不是全局唯一的。