Reactjs-协调(Reconsiliation)
组件的props或者state发生变化时react会自动同步UI页面。
- react是如何同步VM的?
- 如何进行优化的?
优化方式1: 批处理【待研究】
React may batch multiple setState() calls into a single update for performance.
优化方式2: 协调differing 算法?
具体是如何降低时间复杂度的呢?待研究
- 看样子是只比较元素本身的类型,类型不同就替换,相同则更新。
- 两个假设都是为了降低时间复杂度了吗?看样子只有假设1是为了降低时间复杂度
https://zhuanlan.zhihu.com/p/51483167 http://echizen.github.io/tech/2019/04-06-react-fiber https://zhuanlan.zhihu.com/p/26027085
Lists and Keys
引入背景
React组件render方法返回的是个React元素构成的树。当组件的props或者state发生变更时,render方法会返回一个新的React元素构成的树。React采用一个启发式(简化的)diffing算法计算出两颗树的差异。详细的得看看React协调。
React用key标记React元素。
关于key的描述
- 必要;
- 唯一;
- 固定。
必要
A “key” is a special string attribute you need to include when creating lists of elements. 列表元素必须要指定
key属性。
没有指定key属性会提示:
Warning: Each child in a list should have a unique "key" prop.
唯一
Keys Must Only Be Unique Among Siblings 兄弟(siblings)之间唯一(不必全局唯一)。
不唯一会提示:
Warning: Encountered two children with the same key,
xxx. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
不唯一造成的影响:
重复key的组件可能会重复渲染或者被忽略(测试[email protected]版本是被渲染的,只是控制台有个warning)。但是行为不保证,未来可能会变更。
总之行为不可预测(unpredictable),不要存在重复key。
固定
key值充当React元素标识符,要能唯一识别数据,并且每次re-render时子元素的key要保持不变,否则影响diffing性能。
使用
语法
key属性的取值可以是任意类型吗?
直接看ts声明:
type Key = string | number;
/**
* @internal You shouldn't need to use this type since you never see these attributes
* inside your component or have to validate them.
*/
interface Attributes {
key?: Key | null | undefined;
}
引用类型会被转成字符串。
优先valueOf,没有再toString:
function NumberList(props) {
const { numbers } = props;
const listItems = numbers.map(number =>
<li key={CustomerKey(number)}>{number}</li> // 相当于 CustomerKey(number) + ''
);
return (
<ul>{listItems}</ul>
)
}
function CustomerKey(number) {
return {
toString: () => {
console.log(`toString`)
return number;
},
valueOf: () => {
console.log(`valueOf`)
return number * 2;
}
}
}
何如提升性能的?
复用子组件实例,避免重复销毁-创建组件实例。即在re-render过程中如果key不变,则复用之前的组件实例(组件实例状态不变)。
Demo:
// 旧v-dom
<ul>
<li key="first">first</li>
<li key="second">second</li>
</ul>
// 新v-dom
<ul>
<li key="zero">zero</li>
<li key="first">first</li>
<li key="second">second</li>
</ul>
React只创建元素<li key="zero">zero</li>插入到列表首部,剩下两个元素复用。
当使用index作为key时,但实际新创建的是index=2的元素,前面两个元素复用(不过prop发生变化)。
不合适的key引发的问题?
经常会看到用数组索引作为key。
- 该复用的没有复用
- diff性能变差。
- 不该复用的复用了
- 如果组件是个非受控组件,会导致组件实例状态不变,可能会产生意向不到的效果。
- 如果是受控组件,并没有影响。但这是埋坑,很难保证以后子组件会不会包含非受控组件。
进阶
动态的key
React元素的key变更等同类型变更,React会视为不同的React元素树,会重新创建组件实例。
import { useEffect, useState } from "react";
export default function App() {
const [key, setKey] = useState();
return (
<div>
<button onClick={() => setKey(Date.now())}>Update Key</button>
<Foo key={key} />
</div>
);
}
function Foo() {
useEffect(() => {
console.log("Foo.mounted");
return () => {
console.log("Foo.willUnmount");
};
}, []);
return <h2>Foo</h2>;
}
点击【Update Key】按钮都会造成组件Foo重新创建。
索引index作为key行不行?
官方态度
-
When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort.
-
We don’t recommend using indexes for keys if the order of items may change.
-
If you choose not to assign an explicit key to list items then React will default to using indexes as keys. 但是此时React还会给出Warning。
总之:虽然React内部默认采用index作为列表元素的key,但是官方不建议使用索引index作为key。
为啥索引index不能作为key?
索引index表示的是子元素位置,索引index作为key本质是表示子元素的位置能够识别子元素。特定的条件下索引可能能代表元素,但是如果列表数据发生排序、增加或者删除操作会造成,可能会造成错误的渲染效果,以及对性能造成影响。
总之索引index不是合适的key。
- 该复用的没有复用;
- 不该复用的复用了 非受控组件Demo。
非列表元素增加key属性
本质上任何组件都可以声明key属性,只不过非列表的元素key属性不是必须的。不管是否是列表元素key属性的用法是一致的。
如果子元素发生位置变化(类似列表发生排序),也可以利用key属性复用子组件实例。
Demo:
import { useEffect, useState } from "react"
export default () => {
const [swapped, setSwapped] = useState();
return (
<div>
<button onClick={() => setSwapped(swapped => !swapped)}>Swap</button>
{
swapped
? (
<>
<Right key="right"/>
<Left key="left"/>
</>
)
: (
<>
<Left key="left" />
<Right key="right" />
</>
)
}
</div>
)
}
Issues/Concern
-
Use unique and constant keys when rendering dynamic children, or expect strange things to happen. 动态子元素应该增加
key? -
怎样理解
key is not really about performance, it's more about identity