mini-react 第一篇:从零到一实现最最最基本的 mini-react
mini-react 第二篇:从一到二实现 fiber 架构
一、什么是统一提交?
当初次接触到新的名词时,总是烦躁怎么会造出这么多花里胡哨的东西?等用惯了,多好的名字哇,统一的定义,无需再赘言原理。(当然不包括万恶的黑话)
统一提交是指,在完成 dom 的创建后,将挂载的动作统一放到最后来执行,一次性执行完毕。
但为什么有这种方案的出现呢?
思考这样一个场景:由于某个 dom 的挂载带着非常炫酷的动画,或者在某个 dom 挂载后,js 引擎又被插入了各种任务,于是 js 引擎繁忙中,requestIdleCallback 一直不被调用,完整的页面就只展示到了这个 dom 就此暂停,等待许久才可能进入下一个 dom 的挂载。用户:摇头.jpg
统一提交就可以有效解决这个问题。反正都是等,白屏总比只显示一半好(对于白屏,用户可能以为是网络不好,可只显示一半锅就全全丢到开发身上了)。
二、实现思路
- 将挂载 dom 的内容抽取到一个新的函数
commitWork(fiber)中 => 这是挂载单个 dom 节点的函数 - 挂载所有节点可通过
commitWork遍历 fiber 架构实现 => 递归遍历 child 与 sibling - 由于是统一提交,一次性完成所有 dom 的挂载,而非等待浏览器空闲时期挂载,所以需要一个总的入口函数
commitRoot,里面使用commitWork - 统一提交的时机为所有虚拟节点对应的 dom 都创建完毕,由上一篇从一到二实现 fiber 架构可知,是
nextUnitOfWork的值为undefined的时候
三、代码实现
/**
* 普通组件
* @param {Object} vnode
*/
function updateHostComponent(fiber) {
if (!fiber.dom) {
// 1. 创建 DOM 节点
const dom = fiber.dom =
fiber.type === "ELEMENT_TEXT"
? document.createTextNode("")
: document.createElement(fiber.type);
// 2. 赋值 props
for (const key in fiber.props) {
if (key === "children") continue;
dom[key] = fiber.props[key];
}
}
// 3. 处理 props.children
initChildren(fiber, fiber.props.children)
/** 移至 commitWork 函数内
// 4. 挂载
let parentFiber = vnode.parent;
if (parentFiber) {
while (!parentFiber.dom) {
parentFiber = parentFiber.parent;
}
parentFiber.dom.appendChild(vnode.dom);
}
*/
}
function workLoop(deadline) {
let shouldYield = false
// nextUnitOfWork 不为空的判断补上,否则总会再次调用 performUnitOfWork
if (!shouldYield && nextUnitOfWork) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
shouldYield = deadline.timeRemaining() < 1
}
// 统一提交的时机
if (!nextUnitOfWork && root) {
commitRoot()
}
requestIdleCallback(workLoop)
}
/**
* 提交入口
*/
function commitRoot() {
commitWork(root.child)
// 提交完毕需清空 root,否则重复提交
root = null
}
/**
* 一次挂载
*/
function commitWork(fiber) {
if (!fiber) return;
let parentFiber = fiber.parent;
if (parentFiber) {
while (!parentFiber.dom) {
parentFiber = parentFiber.parent;
}
fiber.dom && parentFiber.dom.appendChild(fiber.dom);
}
// 遍历
commitWork(fiber.child)
commitWork(fiber.sibling)
}