React中的DOM-DIFF / Fiber算法
jsx或者说React的渲染机制:
- 把jsx语法编译为虚拟DOM「virtualDOM」
- 基于babel-preset-react-app把jsx视图编译为 React.createElement 格式
React.createElement(标签名/组件名,props,…children) - createElement 执行就会创建出对应的 virtualDOM
- 基于babel-preset-react-app把jsx视图编译为 React.createElement 格式
- 基于ReactDOM 中的render 方法,把virtualDOM编译为真实DOM,最后把真实的DOM交给浏览器渲染
- 当组件更新的时候:
- 会根据最新的数据,重新把”整个jsx“编译为新的 virtualDOM 「不论有的地方的数据是否发生改变,virtualDOM都是从头编译到尾的」
- 但是不会把整个virtualDOM全部编译为真实的DOM,它需要经过一个 DOM-DIFF 的对比,把virtualDOM中差异的部分获取到,接下来只把差异的部分渲染为真实的DOM,交给浏览器渲染
React DOM-DIFF 算法在react不同版本中的实现:
- 在ReactV16及以前:新老虚拟DOM对比
- 在ReactV17及以后:老的DOM会构建出Fiber链表,拿最新创建的虚拟DOM和Fiber链表做对比,计算出差异的部分。
DOM-DIFF 主要就是在组件更新的时候,可以实现差异化的更新而不是整体全部更新,以此来优化组件渲染的速度,提高性能。
优化原则:
- 深度优先原则
- 同级对比
- 不同类型的元素,会产出不同的结构:销毁老结构,创建新结构
- 可以通过key标识移动的元素:如果不设置key,则默认元素的“索引”就是key
处理规则:
详细的处理步骤:
举例演示 && 原理说明:
import React, { useState, useEffect } from "react";
import styled from "styled-components";
const TestBox = styled.div`
display: flex;
div{
margin-right: 10px;
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
background: lightpink;
font-size: 18px;
}
`;
let n = 0;
const Test = function Test() {
n++;
let [state, setState] = useState(['A', 'B', 'C', 'D', 'E', 'F']);
useEffect(() => {
setTimeout(() => {
setState(['A', 'C', 'E', 'B', 'G', 'F']);
}, 2000);
}, []);
return <TestBox>
{state.map(item => {
return <div key={item}>
{n > 1 ? `${item}-NEW` : item}
</div>;
})}
</TestBox>;
};
export default Test;
关于索引作为key的优化
import React, { useState, useEffect } from "react";
import styled from "styled-components";
const TestBox = styled.div`
display: flex;
div{
margin-right: 10px;
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
background: lightpink;
font-size: 18px;
}
`;
const Test = function Test() {
let [state, setState] = useState(['A', 'B', 'C', 'D', 'E', 'F']);
useEffect(() => {
setTimeout(() => {
setState(['A', 'E', 'B', 'G', 'F']);
}, 2000);
}, []);
return <TestBox>
{state.map((item, index) => {
return <div key={index}>
{item}
</div>;
})}
</TestBox>;
};
export default Test;
循环创建元素的时候需要设置唯一的key,我们尽可能的不要用索引作为key值,而是用一个不会因为“位置或索引”改变而改变的值做key「例如:每一项唯一的ID值等」