/** @jsxRuntime classic */
//**============== */
const Didact = {
createElement,
render,
};
function createTextElement(text) {
return {
type: "TEXT_ELEMENT",
props: {
nodeValue: text,
children: [],
},
};
}
function createElement(type, props, ...children) {
return {
type,
props: {
...props,
children: children.map((child) =>
typeof child === "object" ? child : createTextElement(child)
),
},
};
}
/** @jsx Didact.createElement */
const element = (
);
// const element = Didact.createElement(
// "div",
// { id: "foo" },
// Didact.createElement("a", null, "bar"),
// Didact.createElement("b")
// );
function createDom(fiber) {
const dom =
fiber.type == "TEXT_ELEMENT"
? document.createTextNode("")
: document.createElement(fiber.type);
const isProperty = (key) => key !== "children";
Object.keys(fiber.props)
.filter(isProperty)
.forEach((name) => {
dom[name] = fiber.props[name];
});
return dom;
}
function commitRoot() {
commitWork(wipRoot.child);
wipRoot = null;
}
function commitWork(fiber) {
if (!fiber) {
return;
}
const domParent = fiber.parent.dom;
domParent.appendChild(fiber.dom);
commitWork(fiber.child);
commitWork(fiber.sibling);
}
let nextUnitOfWork = null;
let wipRoot = null;
let currentRoot = null;
function workLoop(deadline) {
let shouldYield = false;
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
shouldYield = deadline.timeRemaining() < 1;
}
if (!nextUnitOfWork && wipRoot) {
commitRoot();
}
requestIdleCallback(workLoop);
}
requestIdleCallback(workLoop);
//获取工作的第一个节点 执行工作,而且返回下一个工作单元
function performUnitOfWork(fiber) {
if (!fiber.dom) {
fiber.dom = createDom(fiber);
}
const elements = fiber.props.children;
let index = 0;
let prevSibling = null;
while (index < elements.length) {
const element = elements[index];
const newFiber = {
type: element.type,
props: element.props,
parent: fiber,
dom: null,
};
if (index === 0) {
fiber.child = newFiber;
} else {
prevSibling.sibling = newFiber;
}
prevSibling = newFiber;
index++;
}
if (fiber.child) {
return fiber.child;
}
let nextFiber = fiber;
while (nextFiber) {
if (nextFiber.sibling) {
return nextFiber.sibling;
}
nextFiber = nextFiber.parent;
}
}
function render(element, container) {
wipRoot = {
dom: container,
props: {
children: [element],
},
alternate: currentRoot,
};
nextUnitOfWork = wipRoot;
}
const container = document.getElementById("root");
Didact.render(element, container);
React 17 版本开始提供了一个新的JSX转换方案。在之前,JSX 代码会被默认编译成 React.createElement(...)
,而使用新 JSX 方案后,会从一个独立的模块react/jsx-runtime
中引入 JSX 函数。
babel插件@babel/plugin-transform-react-jsx
/** @jsxRuntime classic */ babel运行时转换
render 创建最初始的fiber对象,render执行完之后会执行workLoop方法,开发fiber任务
function render(element, container) {
wipRoot = {
dom: container,
props: {
children: [element],
},
alternate: currentRoot,
};
nextUnitOfWork = wipRoot;
}
如果存在下一个fiber任务那么就行执行performUnitOfWork,如果没有说明fiber树构建完毕,执行commitRoot 提交进行页面渲染。
function workLoop(deadline) {
let shouldYield = false;
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
shouldYield = deadline.timeRemaining() < 1;
}
if (!nextUnitOfWork && wipRoot) {
commitRoot();
}
requestIdleCallback(workLoop);
}
performUnitOfWork构建fiber树且返回下一个fiber任务
//获取工作的第一个节点 执行工作,而且返回下一个工作单元
function performUnitOfWork(fiber) {
if (!fiber.dom) {
fiber.dom = createDom(fiber);
}
const elements = fiber.props.children;
let index = 0;
let prevSibling = null;
while (index < elements.length) {
const element = elements[index];
const newFiber = {
type: element.type,
props: element.props,
parent: fiber,
dom: null,
};
if (index === 0) {
fiber.child = newFiber;
} else {
prevSibling.sibling = newFiber;
}
prevSibling = newFiber;
index++;
}
if (fiber.child) {
return fiber.child;
}
let nextFiber = fiber;
while (nextFiber) {
if (nextFiber.sibling) {
return nextFiber.sibling;
}
nextFiber = nextFiber.parent;
}
}
fiber对象和真实的dom对象挂钩。
if (!fiber.dom) {
fiber.dom = createDom(fiber);
}
处理fiber对象的子元素,且为对应的子元素创建fiber对象,这个时候子元素fiber对象的dom属性还是空。如果是第一个子元素,那么把第一个子元素的fiber对象挂到父fiber对象的child属性上。如果不是父fiber的第一个子元素,那么把该fiber对象挂到它前面一个fiber对象的sibling属性上。(也就是只创建了父fiber直接子元素对应的fiber对象,但是还没有处理子元素对应的fiber任务)
let index = 0;
let prevSibling = null;
while (index < elements.length) {
const element = elements[index];
const newFiber = {
type: element.type,
props: element.props,
parent: fiber,
dom: null,
};
if (index === 0) {
fiber.child = newFiber;
} else {
prevSibling.sibling = newFiber;
}
prevSibling = newFiber;
index++;
}
返回下一个fiber任务,如果有子元素,那么处理第一个子元素。如果没有子元素,那么处理它兄弟节点,如果没有兄弟节点,那么返回父节点。然后寻找父元素的兄弟节点,一直找到最初始节点。当performUnitOfWork没有可以返回的节点,那么说明fiber树构建完成,执行commitRoot渲染页面。(每一个fiber任务处理完都可以中断,如果恢复,那就是继续处理nextUnitOfWork。所以fiber任务的终中断和回复是在fiber树构建阶段)
if (fiber.child) {
return fiber.child;
}
let nextFiber = fiber;
while (nextFiber) {
if (nextFiber.sibling) {
return nextFiber.sibling;
}
nextFiber = nextFiber.parent;
}
页面渲染:
commitWork(wipRoot.child); //从最外层开始
wipRoot = null;//开始渲染要把wipRoot置为null,因为下一次页面刷新还会重新构建。
const domParent = fiber.parent.dom;
domParent.appendChild(fiber.dom);
commitWork(fiber.child);
commitWork(fiber.sibling);
通过递归添加子dom节点到父dom上面。最后完成渲染。(这个时候是无法中断的)
function commitRoot() {
commitWork(wipRoot.child);
wipRoot = null;
}
function commitWork(fiber) {
if (!fiber) {
return;
}
const domParent = fiber.parent.dom;
domParent.appendChild(fiber.dom);
commitWork(fiber.child);
commitWork(fiber.sibling);
}