组件实例的反应协调
React Reconciliation Of Component Instances
我一直在尝试理解 React 协调,但我对差异算法如何工作的一些细节感到非常困惑。到目前为止,我了解到每当进行更新时,我们都会创建一个新的反应元素树并将其与我们之前的反应元素树进行比较。差异算法设法找到新旧反应元素树之间的差异。算法的 2 个假设。相同级别和类型的元素不需要卸载和重新安装,并且键提供了一种不通过索引识别子元素的方法。
让我感到困惑的部分是如何在 2 个反应实例之间进行比较。例如,比较旧反应元素树中的 <Comp1/>
和新反应元素树中的 <Comp2/>
时(假设 <Comp2>
在创建新树时替换了 <Comp1>
) ,差异算法是否只是比较两个反应元素的“类型”属性?因此,如果两者具有相同的“类型”,则差异算法不会考虑卸载和安装到 DOM?
does the diffing algorithm simply compare both react elements' "type"
attributes?
是的,来自docs:
Whenever the root elements have different types, React will tear down
the old tree and build the new tree from scratch. Going from <a>
to
<img>
, or from <Article>
to <Comment>
, or from <Button>
to <div>
- any
of those will lead to a full rebuild.
你的另一个问题:
So if both have the same "type", then the diffing algorithm doesn't
consider un-mounting and mounting into the DOM?
是的,在那种情况下,React 只是 更新 现有实例。在组件更新期间,实例保持不变,状态在渲染中保持不变。
你可以在下面的例子中看到:
- 我们渲染的第一个地方
A
,当我们用另一个 A
替换它时它不会被卸载(因为相同类型)。
- 我们使用
A
的第二个地方,一旦我们将其替换为B
,反应就会卸载A
。
let A = (props) => {
React.useEffect(() => {
console.log('Mounted A', props);
return () => {
console.log('Unmounted A', props);
};
}, []);
return <div>This is A: {props.tmp}</div>;
};
let B = (props) => {
React.useEffect(() => {
console.log('Mounted B', props);
return () => {
console.log('Unmounted B', props);
};
}, []);
return <div>This is B</div>;
};
function App() {
let [tmp, setTemp] = React.useState(0);
return (
<div
onClick={() => {
setTemp(tmp + 1);
}}
>
{tmp % 2 == 0 ? <A id="first A"/> : <A id="second A"/>}
{tmp % 2 == 0 ? <A id="third A"/> : <B />}
<p>Start editing to see some magic happen :)</p>
</div>
);
}
ReactDOM.render(<App />,document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
我一直在尝试理解 React 协调,但我对差异算法如何工作的一些细节感到非常困惑。到目前为止,我了解到每当进行更新时,我们都会创建一个新的反应元素树并将其与我们之前的反应元素树进行比较。差异算法设法找到新旧反应元素树之间的差异。算法的 2 个假设。相同级别和类型的元素不需要卸载和重新安装,并且键提供了一种不通过索引识别子元素的方法。
让我感到困惑的部分是如何在 2 个反应实例之间进行比较。例如,比较旧反应元素树中的 <Comp1/>
和新反应元素树中的 <Comp2/>
时(假设 <Comp2>
在创建新树时替换了 <Comp1>
) ,差异算法是否只是比较两个反应元素的“类型”属性?因此,如果两者具有相同的“类型”,则差异算法不会考虑卸载和安装到 DOM?
does the diffing algorithm simply compare both react elements' "type" attributes?
是的,来自docs:
Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch. Going from
<a>
to<img>
, or from<Article>
to<Comment>
, or from<Button>
to<div>
- any of those will lead to a full rebuild.
你的另一个问题:
So if both have the same "type", then the diffing algorithm doesn't consider un-mounting and mounting into the DOM?
是的,在那种情况下,React 只是 更新 现有实例。在组件更新期间,实例保持不变,状态在渲染中保持不变。
你可以在下面的例子中看到:
- 我们渲染的第一个地方
A
,当我们用另一个A
替换它时它不会被卸载(因为相同类型)。 - 我们使用
A
的第二个地方,一旦我们将其替换为B
,反应就会卸载A
。
let A = (props) => {
React.useEffect(() => {
console.log('Mounted A', props);
return () => {
console.log('Unmounted A', props);
};
}, []);
return <div>This is A: {props.tmp}</div>;
};
let B = (props) => {
React.useEffect(() => {
console.log('Mounted B', props);
return () => {
console.log('Unmounted B', props);
};
}, []);
return <div>This is B</div>;
};
function App() {
let [tmp, setTemp] = React.useState(0);
return (
<div
onClick={() => {
setTemp(tmp + 1);
}}
>
{tmp % 2 == 0 ? <A id="first A"/> : <A id="second A"/>}
{tmp % 2 == 0 ? <A id="third A"/> : <B />}
<p>Start editing to see some magic happen :)</p>
</div>
);
}
ReactDOM.render(<App />,document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>