https://pomb.us/build-your-own-react/ 을 기반으로 학습 내용을 정리한 글입니다.
STEP 5 - Render and commit Phases
이전 step에서 수정해야 할 코드가 있다.
if (fiber.parent) {
fiber.parent.dom.appendChild(fiber.dom);
}
바로 이 부분인데, element에 대해 작업할 때마다 DOM에 새 노드를 추가하고 있다. 전체 트리 렌더링을 완료하기 전에 브라우저가 작업을 중단할 수 있는데, 이 경우 사용자에게는 완전하지 않은 UI가 표시된다.
따라서 DOM을 변경하는 부분을 제거하고, 대신 fiber tree의 root를 추적할 것이다.
이를 work in progress root, 줄여서 wipRoot라고 부른다.
function render(element, container) {
wipRoot = {
dom: container,
props: {
children: [element],
},
};
nextUntilOfWork = wipRoot;
}
let nextUntilOfWork = null;
let wipRoot = null;
그리고 모든 트리 렌더링이 완료되면 전체 fiber tree를 DOM에 그때 커밋한다.
commitRoot 함수를 만들어 여기서 실행시킬 것이다.
이 함수는 모든 노드를 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);
}
...
function workLoop(deadline) {
let shouldYield = false
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(
nextUnitOfWork
)
shouldYield = deadline.timeRemaining() < 1
}
if (!nextUnitOfWork && wipRoot) {
commitRoot()
}
requestIdleCallback(workLoop)
}
즉, STEP 5을 완료한 상태의 전체 코드는 아래와 같다.
function createElement(type, props, ...children) {
return {
type,
props: {
...props,
children: children.map((child) =>
typeof child === "object" ? child : createTextElement(child)
),
},
};
}
function createTextElement(text) {
return {
type: "TEXT_ELEMENT",
props: {
nodeValue: text,
children: [],
},
};
}
function createDom(fiber) {
const dom =
element.type === "TEXT_ELEMENT"
? document.createTextNode("")
: document.createElement(element.type);
const isProperty = (key) => key !== "children";
Object.keys(element.props)
.filter(isProperty)
.forEach((name) => {
dom[name] = element.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);
}
function render(element, container) {
wipRoot = {
dom: container,
props: {
children: [element],
},
};
nextUntilOfWork = wipRoot;
}
let nextUntilOfWork = null;
let wipRoot = null;
function workLoop(deadline) {
let shouldYield = false;
while (nextUntilOfWork && !shouldYield) {
nextUntilOfWork = performUnitOfWork(nextUntilOfWork);
shouldYield = deadline.timeRemaining() < 1;
}
if (!nextUntilOfWork && wipRoot) {
commitRoot();
}
requestIdleCallback(workLoop);
}
requestIdleCallback(workLoop);
function performUnitOfWork(fiber) {
if (!fiber.dom) {
fiber.dom = createDom(fiber);
}
if (fiber.parent) {
fiber.parent.dom.appendChild(fiber.dom);
}
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;
}
}
const Didact = {
createElement,
render,
};
/** @jsx Didact.createElement */
const element = (
<div id="foo">
<a>bar</a>
<b />
</div>
);
const container = document.getElementById("root");
Didact.render(element, container);
여기까지 STEP 5 완료!
728x90
'FRONT-END > React' 카테고리의 다른 글
나만의 React 만들기(Build your own react) - STEP 7 (0) | 2024.09.07 |
---|---|
나만의 React 만들기(Build your own react) - STEP 6 (1) | 2024.09.06 |
나만의 React 만들기(Build your own react) - STEP 4 (1) | 2024.09.05 |
나만의 React 만들기(Build your own react) - STEP 3 (0) | 2024.09.05 |
나만의 React 만들기(Build your own react) - STEP 2 (0) | 2024.09.04 |
댓글