# 前置理念
# react设计理念
# 异步可中断
react 15版本的协调过程是同步的(stack reconciler)。这导致了浏览器在处理一些比较耗时的任务的时候不能及时响应一些高优先级的任务比如用户输入等。会造成页面卡顿
在react 16及以后的版本中解决方案是将任务分割成一个个小任务,在执行完没一个小任务的时候可以中断。为了实现这个react中有三个概念
# Fiber
Fiber对应的就是一个个的节点可以把react渲染和更新分割成的一个个的小的单元任务
# Scheduler
调度器区分react中任务的优先级,让高优先级任务先执行、低优先级任务后执行。在执行任务中如果任务执行时间过长甚至会被即将到来的高优先级任务打断。
# Lane
Lane模型中定义了react每一个任务的优先级,除了比较渲染任务和用户交互(比如鼠标点击、键盘输入等)可以让每个Fiber工作单元之间还能比较优先级
# 实现方式
let firstFiber
let nextFiber = firstFiber
let shouldYield = false;
function performUnitOfWork(nextFiber) {
// todo...
return nextFiber.next
}
function workLoop(deadLine) {
while(nextFiber && !shouldYield) {
nextFiber = performUnitOfWork(nextFiber)
shouldYield = deadLine.timeReaming < 1
}
requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)
异步可中断方案大体实现方式如上 requestIdleCallback是浏览器提供的一个api。指在浏览器空闲时执行任务,入参是一个回调函数,但因为这个api在不同浏览器中有严重的兼容性问题所以reatc中并没有用这个api而是自己又重新写了一套。(Schedular)
# 代数效应
代数效应是指分离函数的副作用(hooks) 比如
function getPrice(id) {
return fetch(`xxx.com?id=${id}`).then((res) => res.price)
}
async function getTotalPrice(id1, id2) {
const p1 = await getPrice(id1);
const p2 = await getPrice(id2);
return p1+p2;
}
async function run() {
await getTotalPrice('001', '002')
}
在上面的例子中getPrice是一个异步方法,而后续如果有函数要调用到getPrice就得加上await。直接影响到了 run 和 getTotalPrice
function getPrice(id) {
const p = perform id;
return p;
}
function getTotalPrice(id1, id2) {
return getPrice(id1) + getPrice(id2)
}
try {
getTotalPrice('001', '002')
}handle(id) {
fetch(`xxx.com?id=${id}`).then(res => {
resume with res.price
})
}
如果能有一种语法类似于上面的 perform和handle。当函数执行到perform的时候暂停函数执行并被handle捕获,handle内部获取数据后用resume with返回结果,函数继续执行。这样就能吧副作用分离(redux)
function usePrice(id) {
useEffect((id)=>{
fetch(`xxx.com?id=${productId}`).then((res)=>{
return res.price
})
}, [])
}
function TotalPirce({id1, id2}) {
const p1 = usePrice(id1);
const p2 = usePrice(id2);
return <TotalPirce props={...}>
}
hooks中同样也有类似的思想
# Fiber与Fiber双缓存
在前面提到react中的Fiber。Fiber就是一个个的节点,节点上属性包括key、type、tag以及用于Fiber节点之间相互连接的成Fiber树的一些属性(child、sibling、return)和用来计算当前状态的(pendingProps、memoizedProps、updateQueue等)
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Fiber节点自身的一些属性
this.tag = tag; // 节点类型类型(如FunctionComponent、ClassComponent等)
this.key = key;
this.elementType = null; //对应节点类型(如p、span、div等等)
this.type = null; // func或者class
this.stateNode = null; // 对应的真实dom节点(感觉跟elementType差别不大,root节点该属性只想fiberRootNode)
// Fiber
this.return = null; // 指向父节点
this.child = null; // 指向第一个子节点
this.sibling = null; //指向兄弟节点
this.index = 0;
this.ref = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// Effects
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null; // 指向正在构建的所对应Fiber节点
}
Fiber节点通过child、return、sibling所连接起来树就是Fiber树。在react app运行的时候会构建两个Fiber树。由fiberRootNode节点的current决定当前渲染的是哪一个。
function App() {
return (
<>
<h1>
<p>count</p> ccc
</h1>
</>
)
}
ReactDOM.render(<App />, document.getElementById("root"));
比如上面代码所构建出来的Fiber树如下
← 介绍 react element →