本次课程我们来优化下目前的代码,目前我们在更新子组件的时候其它组件也会更新,会造成不必要的性能浪费。
问题现状
- 初始app.js
import React from "./core/React.js";
let countFoo = 1;
function Foo() {
console.log("foo rerun");
function handleClick() {
countFoo++;
React.update();
}
return (
<div>
<h1>foo</h1>
{countFoo}
<button onClick={handleClick}>click</button>
</div>
);
}
let countBar = 1;
function Bar() {
console.log("bar rerun");
function handleClick() {
countBar++;
React.update();
}
return (
<div>
<h1>bar</h1>
{countBar}
<button onClick={handleClick}>click</button>
</div>
);
}
let countRoot = 1;
function App() {
console.log("app rerun");
function handleClick() {
countRoot++;
React.update();
}
return (
<div>
hi-mini-react count: {countRoot}
<button onClick={handleClick}>click</button>
<Foo></Foo>
<Bar></Bar>
</div>
);
}
export default App;
- 点了按钮之后每一个组件都会进行渲染
- 期望每一个组件单独更新
出现问题的原因
- 更新之后从根节点整颗树进行更新,重新创建根节点
function update() {
nextWorkOfUnit = {
dom: currentRoot.dom,
props: currentRoot.props,
alternate: currentRoot
}
wipRoot = nextWorkOfUnit
}
解决办法
- 开始当前更新的组件
let wipFiber = null;
function updateFunctionComponent(fiber) {
wipFiber = fiber
const children = [fiber.type(fiber.props)]
reconcileChildren(fiber, children)
}
function update() {
console.log(`wipFiber`, wipFiber)
nextWorkOfUnit = {
dom: currentRoot.dom,
props: currentRoot.props,
alternate: currentRoot
}
wipRoot = nextWorkOfUnit
}
- 发现问题不管点是foo还是bar都打印出来的type是bar,因为目前定义是全局的变量
- 使用闭包来解决这个问题
function update() {
let currentFiber = wipFiber
return () => {
console.log(`currentFiber`, currentFiber)
nextWorkOfUnit = {
dom: currentRoot.dom,
props: currentRoot.props,
alternate: currentRoot
}
nextWorkOfUnit = wipRoot
}
}
- 这样修改之后使用的地方app.jsx也需要修改
import React from "./core/React.js";
let countFoo = 1;
function Foo() {
console.log("foo rerun");
const update = React.update();
function handleClick() {
countFoo++;
update();
}
return (
<div>
<h1>foo</h1>
{countFoo}
<button onClick={handleClick}>click</button>
</div>
);
}
let countBar = 1;
function Bar() {
console.log("bar rerun");
const update = React.update();
function handleClick() {
countBar++;
update();
}
return (
<div>
<h1>bar</h1>
{countBar}
<button onClick={handleClick}>click</button>
</div>
);
}
let countRoot = 1;
function App() {
console.log("app rerun");
const update = React.update();
function handleClick() {
countRoot++;
update();
}
return (
<div>
hi-mini-react count: {countRoot}
<button onClick={handleClick}>click</button>
<Foo></Foo>
<Bar></Bar>
</div>
);
}
export default App;
- 调整指针不在wipRoot不在指向根节点
function update() {
let currentFiber = wipFiber
return () => {
console.log(`currentFiber`, currentFiber)
wipRoot = {
...currentFiber,
alternate: currentFiber
}
nextWorkOfUnit = wipRoot
}
}
- 结束点遍历完整颗树,当处理兄弟节点的时候,只执行一次
function workLoop(deadline) {
let shouldYield = false
while (!shouldYield && nextWorkOfUnit) {
// 返回下一个任务
nextWorkOfUnit = performWorkOfUnit(nextWorkOfUnit)
if (wipRoot?.sibling?.type === nextWorkOfUnit?.type) {
console.log('hit', wipRoot, nextWorkOfUnit)
}
shouldYield = deadline.timeRemaining() < 1
}
if (!nextWorkOfUnit && wipRoot) {
commitRoot()
}
requestIdleCallback(workLoop)
}