一个示例检验对 React Diff 的理解
September 10, 2021React
前言
React Diff 算法为了将复杂度降低到 O(n),提出了以下几种优化方式:
- 只对比统一层级的节点
- 只对同一种类型对比
- 只对同样的 key 对比(没有设置就都认为 key 是 undefined)
要实现的例子如下,通过点击 “btn” 按钮可以切换显示隐藏 “a” 这个组件。通过这个例子我们可以观察不同实现方式对子组件的影响。根据子组件挂载和卸载打印的日志,你能清楚的知道为什么输出内容不一样你就算学明白 React Diff 机制了。
我们先实现 a/b/c 的公共组件,它在挂载和卸载的时候会打印一下 log。
function Comp({ name }) {
useEffect(() => {
console.log("mount:", name)
return () => {
console.log("remove:", name)
}
}, [])
return <div>{name}</div>
}
观察以下几种实现的差异
方式一
常规实现方式:
const App = () => {
const [isShow, setShow] = useState(true)
return (
<div>
<button onClick={() => setShow(!isShow)}>btn</button>
{isShow && <Comp name="a" />}
<Comp name="b" />
<Comp name="c" />
</div>
)
}
点击两次按钮查看日志:
# 挂载 >>>
mount: a
mount: b
mount: c
# 点击1 >>>
remove: a
# 点击2 >>>
mount: a
方式二
此实现方式和上一个 demo 有什么区别呢?
const App = () => {
const [isShow, setShow] = useState(true)
if (isShow) {
return (
<div>
<button onClick={() => setShow(!isShow)}>btn</button>
<Comp name="a" />
<Comp name="b" />
<Comp name="c" />
</div>
)
}
return (
<div>
<button onClick={() => setShow(!isShow)}>btn</button>
<Comp name="b" />
<Comp name="c" />
</div>
)
}
点击两次按钮查看日志:
# 挂载 >>>
mount: a
mount: b
mount: c
# 点击1 >>>
remove: c
# 点击2 >>>
mount: c
方式三
不同类型在位置变化的情况下是如何执行?
const Comp1 = (props) => <Comp {...props} />
const Comp2 = (props) => <Comp {...props} />
const Comp3 = (props) => <Comp {...props} />
const App = () => {
const [isShow, setShow] = useState(true)
if (isShow) {
return (
<div>
<button onClick={() => setShow(!isShow)}>btn</button>
<Comp1 key="a" name="a" />
<Comp2 name="b" />
<Comp3 name="c" />
</div>
)
}
return (
<div>
<button onClick={() => setShow(!isShow)}>btn</button>
<Comp2 name="b" />
<Comp3 name="c" />
</div>
)
}
点击两次按钮查看日志:
# 挂载 >>>
mount: a
mount: b
mount: c
# 点击1 >>>
remove: a
remove: b
remove: c
mount: b
mount: c
# 点击2 >>>
remove: b
remove: c
mount: a
mount: b
mount: c
方式四
加上 key 之后的变化:
const Comp1 = (props) => <Comp {...props} />
const Comp2 = (props) => <Comp {...props} />
const Comp3 = (props) => <Comp {...props} />
const App = () => {
const [isShow, setShow] = useState(true)
if (isShow) {
return (
<div>
<button onClick={() => setShow(!isShow)}>btn</button>
<Comp1 name="a" />
<Comp2 key="b" name="b" />
<Comp3 key="c" name="c" />
</div>
)
}
return (
<div>
<button onClick={() => setShow(!isShow)}>btn</button>
<Comp2 key="b" name="b" />
<Comp3 key="c" name="c" />
</div>
)
}
点击两次按钮查看日志:
# 挂载 >>>
mount: a
mount: b
mount: c
# 点击1 >>>
remove: a
# 点击2 >>>
mount: a