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 属性值
相关推荐
Eward-an5 小时前
LeetCode 1980 题通关指南|3种解法拆解“找唯一未出现二进制串”问题,附Python最优解实现
python·算法·leetcode
程序员酥皮蛋6 小时前
hot 100 第四十题 40.二叉树的层序遍历
数据结构·算法·leetcode
木斯佳7 小时前
HarmonyOS 6实战:从爆款vlog探究鸿蒙智能体提取关键帧算法
算法·华为·harmonyos
Mr.朱鹏7 小时前
JVM-GC垃圾回收案例
java·jvm·spring boot·算法·spring·spring cloud·java-ee
WJSKad12358 小时前
【DepthPro】实战教程:单目深度估计算法详解与应用
算法
wzqllwy8 小时前
8 大经典排序算法(Java 实现):原理 + Demo + 核心分析
java·算法·排序算法
We་ct8 小时前
LeetCode 77. 组合:DFS回溯+剪枝,高效求解组合问题
开发语言·前端·算法·leetcode·typescript·深度优先·剪枝
重生之我是Java开发战士8 小时前
【递归、搜索与回溯】二叉树中的深度优先搜索:布尔二叉树,求根节点到叶节点数字之和,二叉树剪枝,验证二叉搜索树,第K小的元素,二叉树的所有路径
算法·深度优先·剪枝
篮l球场8 小时前
矩阵置零
算法