React - 验证 Diffing 算法、key 的作用

一、验证 Diffing 算法

html 复制代码
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>验证 Diffing 算法</title>
	</head>

	<body>
		<div id="test"></div>
	</body>

	<script src="../js/react17.0/react.development.js" type="text/javascript"></script>
	<script src="../js/react17.0/react-dom.development.js" type="text/javascript"></script>
	<script src="../js/react17.0/babel.min.js" type="text/javascript"></script>
	<script type="text/babel">
		class Time extends React.Component {
			state = { date: new Date() };

			componentDidMount() {
				setInterval(() => {
					this.setState({
						date: new Date(),
					});
				}, 1000);
			}

			// 这里更新的最小粒度是文本节点:{this.state.date.toTimeString()}
			// 在两个输入框中输入内容,内容都不会因为更新而改变
			render() {
				return (
					<div>
						<input type="text" />
						<span>
							现在是:{this.state.date.toTimeString()}
							<input type="text" />
						</span>
					</div>
				);
			}
		}

		ReactDOM.render(<Time />, document.getElementById("test"));
	</script>
</html>

二、key 的作用

1、基本介绍
  1. key 是虚拟 DOM 对象的标识,在更新显示时 key 起着极其重要的作用

  2. 当状态中的数据发生变化时,React 会根据新数据生成新的虚拟 DOM,随后 React 进行新虚拟 DOM 与旧虚拟 DOM 的 diff 比较

    1. 旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key

      • 若虚拟 DOM 中内容没变,直接使用之前的真实 DOM

      • 若虚拟 DOM 中内容变了,则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM

    2. 旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key

      • 根据数据创建新的真实 DOM,随后渲染到到页面
2、用 index 作为 key 可能会引发的问题
  1. 若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实 DOM 更新,界面效果没问题,但效率低

  2. 如果结构中还包含输入类的 DOM:会产生错误的真实 DOM 更新,界面有问题

  • 注:如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表展示,使用 index 作为 key 是没有问题的
3、开发中如何选择 key
  1. 最好使用每条数据的唯一标识作为 key,比如 id、手机号、身份证号、学号等

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

4、演示
html 复制代码
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>key 的作用</title>
	</head>

	<body>
		<div id="test"></div>
	</body>

	<script src="../js/react17.0/react.development.js" type="text/javascript"></script>
	<script src="../js/react17.0/react-dom.development.js" type="text/javascript"></script>
	<script src="../js/react17.0/babel.min.js" type="text/javascript"></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>
						<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 />
						<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>
</html>
  1. 逆序添加,不包含输入类的 DOM

    使用 index 索引值作为 key

    初始数据
    { id: 1, name: "小张", age: 18 }
    { id: 2, name: "小李", age: 19 }
    初始的虚拟 DOM

  2. 小张 --- 18

  3. 小李 --- 19

  4. 更新后的数据
    { id: 3, name: "小王", age: 20 }
    { id: 1, name: "小张", age: 18 }
    { id: 2, name: "小李", age: 19 }
    更新数据后的虚拟 DOM
  5. 小王 --- 20
  6. (对比旧虚拟 DOM 中有没有 key 为 0 的虚拟 DOM,有,对比内容有没有变化,有,渲染)
  7. 小张 --- 18
  8. (对比旧虚拟 DOM 中有没有 key 为 1 的虚拟 DOM,有,对比内容有没有变化,有,渲染)
  9. 小李 --- 19
  10. (对比旧虚拟 DOM 中有没有 key 为 2 的虚拟 DOM,没有,渲染)

    使用 id 唯一标识作为 key

    初始数据
    { id: 1, name: "小张", age: 18 }
    { id: 2, name: "小李", age: 19 }
    初始的虚拟 DOM

  11. 小张 --- 18

  12. 小李 --- 19

  13. 更新后的数据
    { id: 3, name: "小王", age: 20 }
    { id: 1, name: "小张", age: 18 }
    { id: 2, name: "小李", age: 19 }
    更新数据后的虚拟 DOM
  14. 小王 --- 20
  15. (对比旧虚拟 DOM 中有没有 key 为 3 的虚拟 DOM,没有,渲染)
  16. 小张 --- 18
  17. (对比旧虚拟 DOM 中有没有 key 为 1 的虚拟 DOM,有,对比内容有没有变化,没有,复用)
  18. 小李 --- 19
  19. (对比旧虚拟 DOM 中有没有 key 为 2 的虚拟 DOM,有,对比内容有没有变化,没有,复用)

  20. 逆序添加,包含输入类的 DOM

    使用 index 索引值作为 key

    初始数据
    { id: 1, name: "小张", age: 18 }
    { id: 2, name: "小李", age: 19 }
    初始的虚拟 DOM

  21. 小张 --- 18

  22. 小李 --- 19

  23. 输入内容
  24. 小张 --- 18
  25. (输入 10)
  26. 小李 --- 19

  27. 更新后的数据
    { id: 3, name: "小王", age: 20 }
    { id: 1, name: "小张", age: 18 }
    { id: 2, name: "小李", age: 19 }
    更新数据后的虚拟 DOM
  28. 小王 --- 20
  29. (输入 10)(对比旧虚拟 DOM 中有没有 key 为 0 的虚拟 DOM,有,对比内容有没有变化,有,渲染,对比其子虚拟 DOM 有没有变化,没有,复用)
  30. 小张 --- 18
  31. (对比旧虚拟 DOM 中有没有 key 为 1 的虚拟 DOM,有,对比内容有没有变化,有,渲染,对比其子虚拟 DOM 有没有变化,没有,复用)
  32. 小李 --- 19
  33. (对比旧虚拟 DOM 中有没有 key 为 2 的虚拟 DOM,没有,渲染)

    使用 id 唯一标识作为 key

    初始数据
    { id: 1, name: "小张", age: 18 }
    { id: 2, name: "小李", age: 19 }
    初始的虚拟 DOM

  34. 小张 --- 18

  35. 小李 --- 19

  36. 输入内容
  37. 小张 --- 18
  38. (输入 10)
  39. 小李 --- 19

  40. 更新后的数据
    { id: 3, name: "小王", age: 20 }
    { id: 1, name: "小张", age: 18 }
    { id: 2, name: "小李", age: 19 }
    更新数据后的虚拟 DOM
  41. 小王 --- 20
  42. (对比旧虚拟 DOM 中有没有 key 为 3 的虚拟 DOM,没有,渲染)
  43. 小张 --- 18
  44. (输入 10)(对比旧虚拟 DOM 中有没有 key 为 1 的虚拟 DOM,有,对比内容有没有变化,没有,复用,对比其子虚拟 DOM 有没有变化,没有,复用)
  45. 小李 --- 19
  46. (对比旧虚拟 DOM 中有没有 key 为 2 的虚拟 DOM,有,对比内容有没有变化,没有,复用,对比其子虚拟 DOM 有没有变化,没有,复用)

  • 注:子虚拟 DOM 的变化,指的是 input 元素本身,而不是 input 的 value 属性值
相关推荐
CoderCodingNo4 小时前
【NOIP】2011真题解析 luogu-P1003 铺地毯 | GESP三、四级以上可练习
算法
iFlyCai4 小时前
C语言中的指针
c语言·数据结构·算法
查古穆5 小时前
栈-有效的括号
java·数据结构·算法
再一次等风来5 小时前
近场声全息(NAH)仿真实现:从阵列实值信号到波数域重建
算法·matlab·信号处理·近场声全息·nah
汀、人工智能5 小时前
16 - 高级特性
数据结构·算法·数据库架构·图论·16 - 高级特性
大熊背5 小时前
利用ISP离线模式进行分块LSC校正的方法
人工智能·算法·机器学习
kyriewen115 小时前
你点的“刷新”是假刷新?前端路由的瞒天过海术
开发语言·前端·javascript·ecmascript·html5
XWalnut5 小时前
LeetCode刷题 day4
算法·leetcode·职场和发展
蒸汽求职5 小时前
机器人软件工程(Robotics SDE):特斯拉Optimus落地引发的嵌入式C++与感知算法人才抢夺战
大数据·c++·算法·职场和发展·机器人·求职招聘·ai-native
AI成长日志6 小时前
【笔面试算法学习专栏】双指针专题·简单难度两题精讲:167.两数之和II、283.移动零
学习·算法·面试