手写react练习-02
react原理练习将分以下几个流程:
流程一:(01的代码) 是了解虚拟dom的结构是怎么的。我们能否靠着虚拟dom实现渲染。**
流程二:了解 fiber,并懂得fiber 如何构建。 (该章节所处流程)
流程三:了解fiber的双缓存结构,setState的实现, useState的实现
流程四:expiretime解析、 react 的 diff、 ref的原理
fiber
为什么需要fiber
为什么需要fiber ?
对于大型项目,组件树会很大,这个时候递归遍历的成本就会很高,会造成主线程被持续占用,结果就是主线程上的布局、动画等周期性任务就无法立即得到处理,造成视觉上的卡顿,影响用户体验。
- 任务分解的意义,解决上面的问题
- 增量渲染(把渲染任务拆分成块,匀到多帧)
- 更新时能够暂停,终止,复用渲染任务
- 给不同类型的更新赋予优先级
- 并发方面新的基础能力
- 更流畅
1、window.requestIdleCallback()
方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。
了解这个api 后 实现思路是
window.requestIdleCallback() 调用一个wookloop的回调函数,在空闲时间执行渲染
wookloop 按照 构建好的fiber链表执行创建节点, 节点创建好了commit 挂载上dom
let nextUnitWork = null
// 用于记录render阶段Fiber树遍历过程中下一个需要执行的节点。
function wookLoop(idleDeadline) {
while (nextUnitWork && idleDeadline.timeRemaining() > 1) {
console.log('执行当前节点 并返回下一个节点')
nextUnitWork = performNextUnitWork(nextUnitWork)
}
// 这里的工作是把生成好的节点挂到dom上
if (!nextUnitWork && wipRoot) {
commitRoot() // 提交根节点 开始挂载
}
}
requestIdleCallback(wookLoop)
2、关于 performNextUnitWork
performNextUnitWork 的工作是 执行当前fiber(生成当前节点的dom),并返回下一个节点
function performNextUnitWork(workInProgress) {
// todo
const { type } = workInProgress
if (isString(type)) {
// 原生标签
updateHostComponent(workInProgress)
} else if (typeof type === 'function') {
// 渲染函数
type.prototype.isReactComponent
? updateClassComponent(workInProgress)
: updateFunctionComponent(workInProgress)
} else if (type === undefined) {
// 渲染文本节点
updateTextCompoent(workInProgress)
} else if (typeof type === 'symbol') {
// 渲染fragment
updateFragement(workInProgress)
}
// 找子节点
if (workInProgress.child) {
return workInProgress.child
}
let nextFiber = workInProgress
// 找邻节点, 没有就找父的领节点
while (nextFiber) {
if (nextFiber.sibling) {
return nextFiber.sibling
}
nextFiber = nextFiber.return
}
return null
}
3、 commit 节点挂载
let wipRoot = null
function commitRoot() {
commitWorker(wipRoot.child)
wipRoot = null
}
function commitWorker(workInProgress) {
if (!workInProgress) {
return
}
let parentNode = workInProgress.return
while (parentNode) {
if (parentNode.stateNode && !parentNode.stateNode.isReactComponent) {
break
}
parentNode = parentNode.return
}
let parentDom = parentNode && parentNode.stateNode
if (parentDom && workInProgress.stateNode) {
if (!workInProgress.stateNode.isReactComponent) {
parentDom.appendChild(workInProgress.stateNode)
}
}
commitWorker(workInProgress.child)
commitWorker(workInProgress.sibling)
}
4、 协调子节点
// 生成fiber节点
function reconcileChildren(workInProgress, children) {
if (isString(children)) {
return null
}
let newChildren = Array.isArray(children) ? children : [children]
let lastBro = null
for (let i = 0; i < newChildren.length; i++) {
let child = newChildren[i]
let newFiber = {
type: child.type,
props: isString(child) ? child : { ...child.props },
stateNode: null,
sibling: null,
return: workInProgress,
}
if (i === 0) {
workInProgress.child = newFiber
}
if (lastBro) {
lastBro.sibling = newFiber
}
lastBro = newFiber
}
}
5、 不同节点的渲染
function isString(sth) {
return typeof sth === 'string'
}
// 根据vnode,生成node,只有原生标签才会走这个方法
function createNode(workInProgress) {
let node = document.createElement(workInProgress.type)
updateNode(node, workInProgress.props)
return node
}
// 更新属性
function updateNode(node, nextVal) {
Object.keys(nextVal).forEach((k) => {
if (k === 'children') {
if (isString(nextVal[k])) {
node.textContent = nextVal[k]
}
} else {
node[k] = nextVal[k]
}
})
}
function updateFragement(workInProgress) {
let list = []
reconcileChildren(workInProgress, workInProgress.props.children)
}
// 原生标签
function updateHostComponent(workInProgress) {
if (!workInProgress.stateNode) {
workInProgress.stateNode = createNode(workInProgress)
}
// children生成fiber
reconcileChildren(workInProgress, workInProgress.props.children)
}
// 文本
function updateTextCompoent(workInProgress) {
console.log(workInProgress)
if (!workInProgress.stateNode) {
workInProgress.stateNode = document.createTextNode(workInProgress.props)
}
}
// 函数组件
function updateFunctionComponent(workInProgress) {
const { type, props } = workInProgress
const children = type(props)
reconcileChildren(workInProgress, children)
}
// 类组件
function updateClassComponent(workInProgress) {
const { type, props } = workInProgress
const instance = new type(props)
workInProgress.stateNode = instance
const vvnode = instance.render()
reconcileChildren(workInProgress, vvnode)
}
5、导出 render
function render(vnode, container) {
console.log('vnode', vnode) //sy-log
wipRoot = { // 设置根节点
type: 'div',
props: { children: vnode },
stateNode: container,
}
nextUnitWork = wipRoot
// node插入到container中
}