• 【10】基础知识:React - DOM的diffing算法


    在这里插入图片描述

    一、虚拟 DOM 中 key 的作用

    react/vue 中的 key 有什么作用?key的内部原理是什么?

    简单来说:

    key 是虚拟 DOM 对象的标识,在更新显示时 key 起着极其重要的作用,提高渲染效率,防止渲染错误。

    详细的说:

    当状态中的数据发生变化时,React 会根据【新数据】生成【新的虚拟DOM】

    随后 React 进行【新虚拟DOM】与【旧虚拟DOM】的 diff 比较,比较规则如下:

    1、【旧虚拟DOM】中找到了与【新虚拟DOM】相同的 key:
    
    若虚拟 DOM 中内容没变,直接使用之前的真实 DOM
    
    若虚拟 DOM 中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实 DOM
    
    2、旧虚拟DOM中未找到与新虚拟DOM相同的key
    
    根据数据创建新的真实 DOM,随后渲染到到页面
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    “就地复用” 策略:

    当数据项的顺序发生改变时,他不会移动页面上的 DOM 元素匹配数据项顺序的改变,而是简单的复用此处的元素
    
    如果绑定了 key 属性,就可对当前 DOM 元素进行唯一标识,在进行数据更新渲染时,会基于 key 的变化重新排列 DOM 元素的顺序
    
    • 1
    • 2
    • 3

    二、用 index 作为 key 可能会引发的问题

    界面效果没问题, 但效率低:

    若对数据进行,逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实 DOM 更新

    慢动作回放----使用 index 索引值作为 key
    
    根据 diffing 算法规则,所有 DOM 元素都需要都需要重生成新的真实 DOM:
    存在相同key 0 和 1,但虚拟 DOM内容改成了,需要生成新的真实 DOM;
    新增key2,需要生成新的真实 DOM。
    
    初始数据:
    { id: 1, name: '小张', age: 18 },
    { id: 2, name: '小李', age: 19 }
    初始的虚拟 DOM:
    <li key=0>小张---18li>
    <li key=1>小李---19li>
    
    更新后的数据:
    { id: 3, name: '小王', age: 20 },
    { id: 1, name: '小张', age: 18 },
    { id: 2, name: '小李', age: 19 }
    更新数据后的虚拟 DOM:
    <li key=0>小王---20li>
    <li key=1>小张---18li>
    <li key=2>小李---19li>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    界面有问题:

    如果结构中还包含输入类的 DOM,会产生错误 DOM 更新

    慢动作回放----使用 index 索引值作为 key (界面有问题)
    
    根据 diffing 算法规则,内部 input “就地复用”策略顺序会错乱
    
    初始数据:
    { id: 1, name: '小张', age: 18 },
    { id: 2, name: '小李', age: 19 }
    初始的虚拟 DOM:
    
  • 小张---18
  • 小李---19
  • 更新后的数据: { id: 3, name: '小王', age: 20 }, { id: 1, name: '小张', age: 18 }, { id: 2, name: '小李', age: 19 } 更新数据后的虚拟 DOM:
  • 小王---20
  • 小张---18
  • 小李---19
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注意:

    如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用 index 作为 key 是没有问题的。

    三、开发中如何选择key?

    1、最好使用每条数据的唯一标识作为 key , 比如 id、手机号、身份证号、学号等唯一值。

    2、如果确定只是简单的展示数据,用 index 也是可以的。

    四、示例

    DOCTYPE html>
    <html>
    <head>
    	<meta charset="UTF-8">
    	<title>key的作用title>
    head>
    <body>
    	<div id="test">div>
    	
    	<script type="text/javascript" src="../js/react.development.js">script>
    	
    	<script type="text/javascript" src="../js/react-dom.development.js">script>
    	
    	<script type="text/javascript" src="../js/babel.min.js">script>
    
    	<script type="text/babel">		
    		class Person extends React.Component {
    			state = {
    				persons: [
    					{ id: 1, name: '小张', age: 18 },
    					{ id: 2, name: '小李', age: 19 }
    				]
    			}
    
    			add = () => {
    				const { persons } = this.state
    				const p = { id: persons.length + 1, name: '小王', age: 20 }
    				this.setState({ persons: [p, ...persons] })
    			}
    
    			render() {
    				return (
    					<div>
    						<h2>展示人员信息</h2>
    						<button onClick={this.add}>添加一个小王</button>
    						<h3>使用index(索引值)作为key</h3>
    						<ul>
    							{
    								this.state.persons.map((personObj, index) => {
    									return <li key={index}>{personObj.name}---{personObj.age}<input type="text" /></li>
    								})
    							}
    						</ul>
    						<hr />
    						<hr />
    						<h3>使用id(数据的唯一标识)作为key</h3>
    						<ul>
    							{
    								this.state.persons.map((personObj) => {
    									return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text" /></li>
    								})
    							}
    						</ul>
    					</div>
    				)
    			}
    		}
    		
    		ReactDOM.render(<Person />, document.getElementById('test'))
    	script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
  • 相关阅读:
    SpringBoot的学习
    Spring源码分析(八)依赖注入源码解析1:autowire自动注入 和 @Autowired注入
    NLP中常用的utils
    Jenkins | 流水线构建使用expect免密交互时卡住,直接退出
    数据库连不上,解决办法
    tomcat 报收到的cookie头包含无效的cookie
    基于SpringBoot的SSMP整合案例(在Linux中发布项目的注意事项与具体步骤步骤)
    go-zero对数据库的操作
    css样式在弹性盒元素在必要的时候拆行
    C++基础——指针
  • 原文地址:https://blog.csdn.net/weixin_45559449/article/details/133833041