我们将从头开始一步一步重写React。遵循真实的React代码中的架构,但没有所有的优化和非必要的功能
。
createElement
render
Concurrent Mode
- Fibers
- Render 和 Commit 阶段
- Reconciliation
- Function Components
- Hooks
回顾
function render(element, container) {
const dom = document.createElement(element.type)
element.props.children.forEach(child =>
render(child, dom)
)
container.appendChild(dom)
}
回顾上一篇文章中的render
函数,我们发现直到渲染完后,才会停止。如果element
很大,则它可能会阻塞主线程太长时间。而此时如果浏览器需要执行高优先级的操作(例如处理用户输入或保持动画流畅),则必须等到渲染完成才会执行。
众所周知,主流的浏览器的刷新频率为60HZ
,即每16.6ms(1000ms/60HZ)
浏览器就会刷新一次。我们知道JS可以操作DOM
,GUI渲染线程
与JS线程
是互斥的。所以JS脚本执行
和浏览器布局
、绘制
不能同时执行。
为了保证用户体验,在16.6ms
之内,浏览器需要执行以下流程
我们先介绍一个浏览器api requestIdleCallback
-- 在浏览器一帧的剩余空闲时间内执行优先度相对较低的任务。
插播
var lowTasks = 10000
requestIdleCallback(unImportWork)
function unImportWork(deadline) {
while (deadline.timeRemaining() && lowTasks > 0) {
console.log(`执行了${10000 - lowTasks + 1}个任务`)
lowTasks--
}
if (lowTasks > 0) { // 在未来的帧中继续执行
requestIdleCallback(unImportWork)
}
}
deadline
有两个参数
- timeRemaining(): 当前帧还剩下多少时间
- didTimeout: 是否超时
另外 requestIdleCallback
后如果跟上第二个参数 {timeout: ...}
则会强制浏览器在当前帧执行完后执行。
步入正题
经过上面的分析,我们可以得出以下结论 -- 我们将工作 render 为多个单元,在完成每个单元后,如果需要执行其他任何操作,我们将让浏览器中断渲染
let nextUnitOfWork = null
function workLoop(deadline) {
let shouldYield = false
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(
nextUnitOfWork
)
shouldYield = deadline.timeRemaining() < 1
}
requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)
function performUnitOfWork(nextUnitOfWork) {
// TODO
}
通过上面代码,我们把下一个需要处理的单元存储在nextUnitOfWork
中,在浏览器空闲的时候我们再继续处理,这样就实现Concurrent Mode
我们又发现了新的问题,nextUnitOfWork
的结构是什么呢?我们如何创建它?请看下一小节 -- Fibers
未完待续...... 防止走失,请关注作者。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!