-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React 原理学习 #51
Comments
1)背景
2)实现原理
const fiber = {
stateNode, // 节点实例
child, // 子节点
sibling, // 兄弟节点
return, // 父节点
}
|
在处理UI时,如果一次执行太多工作,可能会导致动画丢帧。 基本上,如果React要同步遍历整个组件树,并为每个组件执行任务,它可能会运行超过16毫秒,这将导致不顺畅的视觉效果。 较新的浏览器(和React Native)实现了有助于解决这个问题的API: requestIdleCallback((deadline)=>{
console.log(deadline.timeRemaining(), deadline.didTimeout)
}); 如果我现在打开控制台并执行上面的代码,Chrome会打印
但 不足以实现流畅的UI渲染,因此React团队必须实现[自己的版本](https://github.com/facebook/react/blob/eeb817785c771362416fd87ea7d2a1a32dde9842/packages/scheduler/src/Scheduler.js#L212-L222)。 现在,如果我们将React对组件执行的所有活动放入函数 requestIdleCallback((deadline) => {
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && nextComponent) {
nextComponent = performWork(nextComponent);
}
}); 我们对一个组件执行工作,然后返回要处理的下一个组件。 这是可以做到的,但前提是我们不能同步地处理整个组件树。 因此,我们需要一种方法将渲染工作分解为增量单元。 为了解决这个问题,React必须重新实现遍历树的算法,从依赖于内置堆栈的同步递归模型,变为具有链表和指针的异步模型。 递归遍历walk(a1);
function walk(instance) {
doWork(instance);
const children = instance.render();
children.forEach(walk);
}
function doWork(o) {
console.log(o.name);
} 递归方法直观,非常适合遍历树。 但是正如我们发现的,它有局限性:最大的一点就是我们无法分解工作为增量单元。 我们不能暂停特定组件的工作并在稍后恢复。 通过这种方法,React只能不断迭代直到它处理完所有组件,并且堆栈为空。 链表遍历[Sebastian Markbåge](https://github.com/sebmarkbage)在[[Fiber Principles: Contributing To Fiber](facebook/react#7942) 概括了该算法的要点。 要实现该算法,我们需要一个包含3个字段的数据结构:
在React新的协调算法的上下文中,包含这些字段的数据结构称为Fiber。 下图展示了通过链表链接的对象的层级结构和它们之间的连接类型: function walk(o) {
let root = o;
let current = o;
while (true) {
let child = doWork(current);
if (child) {
current = child;
continue;
}
if (current === root) {
return;
}
while (!current.sibling) {
if (!current.return || current.return === root) {
return;
}
current = current.return;
}
current = current.sibling;
}
} 它看起来像浏览器中的一个调用堆栈。 我们现在通过保持对 function walk(o) {
let root = o;
let current = o;
while (true) {
... current = child;
... current = current.return;
... current = current.sibling;
}
} 我们可以随时停止遍历并稍后恢复。 |
React 18 与 Concurrent Mode对于React来说,有两类瓶颈需要解决: CPU的瓶颈,如大计算量的操作导致页面卡顿 IO的瓶颈,如请求服务端数据时的等待时间 其中CPU的瓶颈通过并发特性的优先级中断机制解决。 IO的瓶颈则交给Suspense解决。 |
react fiber 移除 Effect List 依赖 https://github.com/facebook/react/pull/19673/files |
最精简版 react hooks 实现let state = [];
let setters = [];
let firstRun = true;
let cursor = 0;
function createSetter(cursor) {
return function setterWithCursor(newVal) {
state[cursor] = newVal;
};
}
// This is the pseudocode for the useState helper
export function useState(initVal) {
if (firstRun) {
state.push(initVal);
setters.push(createSetter(cursor));
firstRun = false;
}
const setter = setters[cursor];
const value = state[cursor];
cursor++;
return [value, setter];
}
// Our component code that uses hooks
function RenderFunctionComponent() {
const [firstName, setFirstName] = useState("Rudi"); // cursor: 0
const [lastName, setLastName] = useState("Yardley"); // cursor: 1
return (
<div>
<Button onClick={() => setFirstName("Richard")}>Richard</Button>
<Button onClick={() => setFirstName("Fred")}>Fred</Button>
</div>
);
}
// This is sort of simulating Reacts rendering cycle
function MyComponent() {
cursor = 0; // resetting the cursor
return <RenderFunctionComponent />; // render
}
console.log(state); // Pre-render: []
MyComponent();
console.log(state); // First-render: ['Rudi', 'Yardley']
MyComponent();
console.log(state); // Subsequent-render: ['Rudi', 'Yardley']
// click the 'Fred' button
console.log(state); // After-click: ['Fred', 'Yardley'] |
react因为先天的不足——无法精确更新,所以需要react fiber把组件渲染工作切片;而vue基于数据劫持,更新粒度很小,没有这个压力; |
React 原理学习
JSX -> CE (CreateElement)JSX parser TypeScript Babel React , 或者 React 17 中提出了新的React-JSX JSX-runtime
React-Dom 将创建 DOM 节点递归挂载到 DOM 树上面
现在我们的 React 是没有状态存在的
移除现有的 DOM 节点。
重新渲染一切。
Patch 算法增量更新渲染
调和阶段(Reconciler): 官方解释。React 会自顶向下通过递归,遍历新数据生成新的 Virtual DOM,然后通过 Diff 算法,找到需要变更的元素(Patch),放到更新队列里面去。
渲染阶段(Renderer):遍历更新队列,通过调用宿主环境的API,实际更新渲染对应元素。宿主环境,比如 DOM、Native、WebGL 等。
什么是 Fiber
Fiber 是对 React element 的拷贝,是一份描述 Diff 的工作模拟了函数调用关系
Current 是当前 node , work in progress node 是 copy on write 体现了 immutable 拷贝修改后替换原始数据,计算阶段可中断,提交阶段不可中断。
Element 与渲染无关,只是页面的数据描述,描述组件信息与层级关系
fiber 与渲染有关,fiber 树描述 DIff 等工作调用关系即更新路径,有 react 组件的 render 处理顺序,类似于一棵树转二叉树,线索树指针指向父级,children 直接子节点 ,sibling 相邻节点
Fiber 是如何工作的
ReactDOM.render()
和setState
的时候开始创建更新。Render 是如何执行的
分解任务,加入任务队列,逐一修改,执行 commit 提交
https://zhuanlan.zhihu.com/p/137234573
如何进行 Dom diff
什么是 Reconciler
一个调和器(Reconciler)将3部分个功能结合起来:
Fiber (VirtualDOM): 对组件层级结构的描述
Reconciliation: 计算DOM DIFF
HostConfig :对于React渲染需要的具体端(DOM/React Native等)的封装
改变属性的能力
插入元素的能力
替换元素的能力
The text was updated successfully, but these errors were encountered: